Что находится между ESP и EBP?

Верно, я уверен, что на это неявно ответят много раз, но, похоже, я не могу до конца до конца добраться.

Если у вас есть трассировка стека (x86) (скажем, глядя на нее в WinDbg) и вы смотрите на регистры, что означает, что значения EBP и ESP разнесены на x байтов?

Ссылки:

Чтобы привести пример недавней трассировки стека, которую я имел:

0:016> k
ChildEBP RetAddr  
1ac5ee8c 76b831bb ntdll!NtDelayExecution+0x15
1ac5eef4 76b83a8b KERNELBASE!SleepEx+0x65
1ac5ef04 0060e848 KERNELBASE!Sleep+0xf
1ac5ef10 76859d77 MyApp!application_crash::CommonUnhandledExceptionFilter+0x48 [...\applicationcrash.inc.cpp @ 47]
1ac5ef98 775a0df7 kernel32!UnhandledExceptionFilter+0x127
1ac5efa0 775a0cd4 ntdll!__RtlUserThreadStart+0x62
1ac5efb4 775a0b71 ntdll!_EH4_CallFilterFunc+0x12
1ac5efdc 77576ac9 ntdll!_except_handler4+0x8e
1ac5f000 77576a9b ntdll!ExecuteHandler2+0x26
1ac5f0b0 7754010f ntdll!ExecuteHandler+0x24
1ac5f0b0 6e8858bb ntdll!KiUserExceptionDispatcher+0xf
1ac5f400 74e68ed7 mfc80u!ATL::CSimpleStringT<wchar_t,1>::GetString [f:\dd\vctools\vc7libs\ship\atlmfc\include\atlsimpstr.h @ 548]
1ac5fec0 6e8c818e msvcr80!_NLG_Return [F:\dd\vctools\crt_bld\SELF_X86\crt\prebuild\eh\i386\lowhelpr.asm @ 73]
1ac5ff48 74e429bb mfc80u!_AfxThreadEntry+0xf2 [f:\dd\vctools\vc7libs\ship\atlmfc\src\mfc\thrdcore.cpp @ 109]
1ac5ff80 74e42a47 msvcr80!_callthreadstartex+0x1b [f:\dd\vctools\crt_bld\self_x86\crt\src\threadex.c @ 348]
1ac5ff88 76833677 msvcr80!_threadstartex+0x66 [f:\dd\vctools\crt_bld\self_x86\crt\src\threadex.c @ 326]
1ac5ff94 77569f02 kernel32!BaseThreadInitThunk+0xe
1ac5ffd4 77569ed5 ntdll!__RtlUserThreadStart+0x70
1ac5ffec 00000000 ntdll!_RtlUserThreadStart+0x1b

0:016> r
eax=00000000 ebx=1ac5efc8 ecx=19850614 edx=00000000 esi=1ac5eed0 edi=00000000
eip=7754fd21 esp=1ac5ee8c ebp=1ac5eef4 iopl=0         nv up ei pl nz na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010206

Значения ESP 1ac5ee8c - EBP 1ac5eef4 = 104 байта разницы. Так что там?


person Martin Ba    schedule 22.02.2013    source источник


Ответы (2)


ESP - текущий указатель стека. EBP - это базовый указатель для текущего кадра стека.

Когда вы вызываете функцию, обычно в стеке резервируется место для локальных переменных. На это пространство обычно ссылаются через EBP (все локальные переменные и параметры функций представляют собой известное постоянное смещение от этого регистра на время вызова функции). ESP, с другой стороны, будет изменяться во время вызова функции по мере вызова других функций, или как временное пространство стека, используемое для частичных результатов операции.

Обратите внимание, что большинство компиляторов в наши дни имеют возможность ссылаться на все локальные переменные через ESP. Это освобождает EBP для использования в качестве регистра общего назначения.

В общем, если вы посмотрите на код дизассемблирования в верхней части функции, вы увидите что-то вроде этого:

push EBP
mov  EBP, ESP
sub  ESP, <some_number>

Таким образом, EBP будет указывать на вершину вашего стека для этого кадра, а ESP будет указывать на следующий доступный байт в стеке. (Обычно стеки - но не обязательно - растут в памяти.)

person Community    schedule 22.02.2013
comment
Wrt. В моем примере я проверил в отладчике вызов Sleep: ни SleepEx, ни NtDelayExecution не делают push EBP, и особенно SleepEx кажется нетривиальным. Итак, в моем примере EBP по-прежнему указывает на местоположение стека Sleep, причем все данные из двух вызываемых функций также находятся в стеке. - person Martin Ba; 22.02.2013
comment
Да, возможно, большинство функций WinAPI были построены с опцией компилятора, которая освобождает EBP для общего использования. Т.е. компилятор отслеживает, насколько он изменяет ESP во всей функции, и при необходимости ссылается на локальные переменные относительно ESP с разными смещениями. В этом случае невозможно узнать, точно ли EBP-ESP отражает размер текущего кадра стека. Скорее всего, нет. - person ; 22.02.2013
comment
что такое some_number? Не всегда будет 0? - person Joey.Z; 29.08.2017
comment
@zoujyjs some_number зависит от объема памяти, необходимого во фрейме, а необходимый объем зависит от того, что происходит в функции. - person Dan Barowy; 18.10.2017

Обычно это пространство зарезервировано для локальных переменных, которые в конечном итоге хранятся в стеке. В начале функции ESP уменьшается на соответствующее значение.

В вашем случае в функции есть локальные переменные на 104 байта.

person Frédéric Hamidi    schedule 22.02.2013
comment
104 байта для NtDelayExecution действительно кажутся потрясающими ... но опять же, возможно, показанный стек вызовов сломан ... - person Martin Ba; 22.02.2013
comment
Что ж, 104 байта - это не так уж и много, особенно если NtDelayExecution() по какой-то причине выделяет строковый буфер в стеке. - person Frédéric Hamidi; 22.02.2013
comment
Я перепроверил. Ни SleepEx, ни NtDelayExecution не делают push EBP и особенно SleepEx не кажутся нетривиальными. Итак, в моем примере EBP все еще указывает на расположение стека Sleep. - person Martin Ba; 22.02.2013