Начните с построчного анализа, чтобы выяснить, что делает код .
push 5
Эта инструкция помещает постоянное значение "5" в стек. Зачем? Ну, потому что ...
call ??_U@YAPAXI@Z ; operator new[](uint)
Эта инструкция вызывает operator new[]
, которая принимает один uint
параметр. Этот параметр, очевидно, передается в стек в любом соглашении о вызовах, используемом этим кодом. Итак, ясно, что до сих пор мы вызывали operator new[]
для выделения массива размером 5 байт.
В C ++ это будет записано как:
BYTE* eax = new BYTE[5];
Вызов operator new[]
возвращает его значение (указатель на начало выделенного блока памяти) в регистре EAX
. Это общее правило для всех соглашений о вызовах x86 - функции всегда возвращают свой результат в регистр EAX
.
mov [ebp+esi*4+var_14], eax
Приведенный выше код сохраняет (mov
es) результирующий указатель (тот, который возвращается в EAX
) в ячейку памяти, адресуемую EBP + (ESI * 4) + var_14
. Другими словами, он масштабирует значение в регистре ESI
на 4 (предположительно, размер uint
), добавляет смещение из регистра EBP
, а затем добавляет смещение константы var_14
.
Это примерно эквивалентно следующему псевдо-C ++ коду:
void* address = (EBP + (ESI * 4) + var_14);
*address = eax;
add esp, 4
Это очищает стек, фактически отменяя начальную push 5
инструкцию.
push
поместил 32-битное (4 байта) значение в стек, которое уменьшило указатель стека, который поддерживается в регистре ESP
(обратите внимание, что стек увеличивается на x86 ). Эта add
инструкция увеличивает указатель стека (опять же, регистр ESP
) на 4 байта.
Балансировка стека таким способом является оптимизацией. Вы могли бы эквивалентно написать pop eax
, но это привело бы к дополнительному побочному эффекту, заключающемуся в зацикливании значения в регистре EAX
.
Не существует прямого эквивалента этой инструкции на языке C ++, поскольку она просто выполняет бухгалтерскую работу, которая обычно скрыта от вас языком высокого уровня.
inc esi
Это увеличивает значение регистра ESI
на 1. Это эквивалентно:
esi += 1;
mov byte ptr [eax+4], 0
Сохраняет постоянное значение 0 в блоке памяти размера BYTE в EAX + 4
. Это соответствует следующему псевдо-C ++:
BYTE* ptr = (eax + 4);
*ptr = 0;
cmp esi, 4
Сравнивает значение регистра ESI
с постоянным значением 4. Инструкция CMP
фактически устанавливает флаги, как если бы вычитание было сделано.
Поэтому следующая инструкция:
jl short loc_1C1D40
условно переходит, если значение регистра ESI
равно меньше 4.
Сравнение и переход - это отличительная черта циклической конструкции на языке более высокого уровня, такой как цикл for
или while
.
Собрав все воедино, вы получите что-то вроде:
void Foo(char** var_14)
{
for (int esi = 0; esi < 4; ++esi)
{
var_14[esi] = new char[5];
var_14[esi][4] = 0;
}
}
Это не совсем верно, конечно. Воссоздание исходного кода C или C ++ из скомпилированной сборки очень похоже на воссоздание исходной коровы из котлеты из говяжьего фарша.
Но это довольно хорошо. Фактически, , если вы скомпилируете вышеуказанную функцию в MSVC, оптимизируя скорость и ориентируясь на 32-разрядную версию x86, вы получите следующую сгенерированную сборку :
void Foo(char**) PROC
push esi
push edi
mov edi, DWORD PTR _var_14$[esp+4]
xor esi, esi
$LL4@Foo:
push 5
call void * operator new[](unsigned int) ; operator new[]
mov DWORD PTR [edi+esi*4], eax
add esp, 4
inc esi
mov BYTE PTR [eax+4], 0
cmp esi, 4
jl SHORT $LL4@Foo
pop edi
pop esi
ret 0
void Foo(char**) ENDP
Это в значительной степени точно так же, как и то, что у вас было в вопросе, при условии, что вы игнорируете пролог и эпилог (которые вы так и не показали в вопросе).
Основное отличие состоит в том, что компилятор применяет довольно очевидную оптимизацию подъема цикла к инструкции MOV
. Вместо исходного кода:
mov [ebp + esi * 4 + var_14], eax
вместо этого он предварительно вычисляет esp + var_14
в прологе, кэшируя результат в свободном регистре EDI
:
mov edi, DWORD PTR _var_14$[esp + 4]
, позволяющий просто загружать инструкцию внутри цикла:
mov DWORD PTR [edi + esi * 4], eax
Понятия не имею, почему ваш код этого не делает или почему он использует EBP
для удержания смещения.