Какие "лишние" 32 байта в стеке Windows?
Я изучаю сборку в Windows и пытаюсь выяснить, какие значения в стеке.
В документации Visual C++
сказано, что значения выше RSP:
- Выделенное пространство
- Сохраненная RBP
- Обратный адрес
- Регистры дома (RCX, RDX, R8, R9)
- Параметры функции
Проблема в том, что в стеке 32 лишних байта, не упомянутых в документации.
В моментальном снимке памяти RSP начинается с 0x0000000000DAF5E0. Цветные прямоугольники:
- Желтый: две 64-битные переменные со значением 9
- Белый: сохраненный старый адрес возврата RBP +
- Синий: параметры функции
- Зеленый: регистрируется домой
- Красный:?
Что это за байты, выделенные красным?
Исходный код MASM, созданный с помощью VS2019, MASM64 и работающий в режиме отладки x64.
Флаги C++: /JMC /permissive- /GS /W3 /Zc:wchar_t /ZI /Gm- /Od /sdl /Fd"x64\Debug\vc142.pdb" /Zc:inline /fp:precision /D "_DEBUG" /D "_CONSOLE" /D "_UNICODE" /D "UNICODE" /errorReport:prompt /WX- /Zc:forScope /RTC1 /Gd /MDd /FC /Fa"x64\Debug\" /EHsc /nologo /Fo"x64\ Отладка \" /Fp"x64\Debug\ConsoleApplication1.pch" / диагностика: столбец
.code
; int64_t StackFrameDemo_(int8_t a, int16_t b, int32_t c, int64_t d, int8_t e, int16_t f, int32_t g, int64_t h)
StackFrameDemo_ proc frame
; prolog
push rbp
.pushreg rbp
; allocate 16 bytes on the stack
sub rsp, 16
.allocstack 16
.endprolog
; save registers to register home
mov qword ptr [rbp+8], rcx
mov qword ptr [rbp+16], rdx
mov qword ptr [rbp+24], r8
mov qword ptr [rbp+32], r9
; save the two variables
mov rax, 9
mov [rsp], rax
mov [rsp+type qword], rax
nop ; set the break point here to view memory
; epilog
add rsp, 16 ; release local stack space
pop rbp ; restore caller's rbp register
ret
StackFrameDemo_ endp
end
1 ответ
Ты забыл сделать mov rbp, rsp
(после push rbp
), Чтобы сделать ОДП указатель кадра в вашем кадре стека.
Ваше "домашнее пространство", также известное как теневое пространство, на 32 байта выше вашего обратного адреса, вы просто не используете его. (И вместо этого нарушая соглашение о вызовах, сохраняя данные относительно некоторого регистра, который может иметь любое значение. В этом случае ваш вызывающий, вероятно, также использовал RBP для устаревшего указателя кадра, поэтому вы, вероятно, просто наступаете на домашнее пространство вызывающего.)
Обратите внимание, что 0xCC
- это значение, которое режим отладки MSVC использует для заражения стека, помогая обнаруживать операции чтения неинициализированной памяти. (И если вы случайно запустите память с этим содержимым, это будет x86int3
инструкция точки останова отладки.)
И, кстати, когда вы используете RBP в качестве традиционного указателя кадра, mov rsp, rbp
/ pop rbp
/ ret
более эффективен, чем add rsp, 16
/ pop rbp
. Немного меньший размер кода, и некоторые процессоры выполняют удаление mov, чтобы избежать необходимости в исполнительном блоке дляmov
. Это могло бы сломаться для вас более шумно, например, возвращение с обратного адреса вашего вызывающего абонента, которое вы могли заметить при пошаговом режиме!
(leave
= mov / pop, так что вы можете использовать это для еще меньшего размера кода. Для производительности это нормально, в отличие отenter
; GCC используетleave
в функциях с указателем кадра, которые заканчиваются RSP, еще не указывающим на сохраненный RBP. Некоторые другие компиляторы предпочитают mov/pop. Но компиляторы обычно используют толькоadd rsp, n
когда они вообще не использовали указатель кадра.)
Указатели кадров не являются обязательными и не являются обязательной частью компоновки стека-кадра. Директивы вроде.allocstack 16
создавать метаданные, которые делают возможным раскручивание стека без традиционного связанного списка указателей фреймов.