Каковы «лишние» 32 байта в стеке Windows? - PullRequest
2 голосов
/ 26 марта 2020

Я изучаю сборку на Windows и пытаюсь выяснить, каковы значения в стеке.
Документация Visual C ++ говорит, что значения выше RSP:

  • Выделенное пространство
  • Сохраненный RBP
  • Адрес возврата
  • Регистр home (RCX, RDX, R8, R9)
  • Параметры функции

Проблема в том, что в стеке есть 32 дополнительных байта, не упомянутых в документации.

В снимок памяти RSP начинается с 0x0000000000DAF5E0. Цветные поля:

  • Желтый: две 64-битные переменные со значением 9
  • Белый: сохраненный старый RBP + адрес возврата
  • Синий: параметры функции
  • Зеленый: регистры home
  • Красный:?

Какими могут быть эти байты в красном?

Исходный код MASM, созданный на VS2019, MASM64 и запущенный в Режим отладки x64.

Флаги C ++: / JM C / permissive- / GS / W3 / Z c: wchar_t / ZI / Gm- / Od / sdl / Fd "x64 \ Debug \ vc142. pdb "/ Z c: встроенный / fp: точный / D" _DEBUG "/ D" _CONSOLE "/ D" _UNICODE "/ D" UNICODE "/ errorReport: prompt / WX- / Z c: forScope / RTC1 / Gd / MDd / F C / Fa "x64 \ Debug \" / EHs c / nolo go / Fo "x64 \ Debug \" /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 Ответ

3 голосов
/ 26 марта 2020

Вы забыли сделать mov rbp, rsp (после push rbp), чтобы сделать RBP указателем кадра на ваш кадр стека.

Ваше "домашнее пространство" или теневое пространство на 32 байта выше вашего обратного адреса, вы просто не используете его. (И вместо этого нарушаете соглашение о вызовах, сохраняя относительно некоторого регистра, который может иметь любое значение. В этом случае ваш вызывающий возможно, также используя RBP для устаревшего указателя кадра, так что вы, вероятно, просто наступаете на домашнее пространство вызывающего абонента.)


Обратите внимание, что 0xCC - это значение MSV C, используемый в режиме отладки для отравления стек, помогающий обнаруживать чтения неинициализированной памяти. (И если вы случайно выполняете память с этим содержимым, это инструкция отладочной точки останова x86 int3.)


И кстати, когда вы используете RBP в качестве традиционного указателя кадра, mov rsp, rbp / pop rbp / ret более эффективно, чем add rsp, 16 / pop rbp. Чуть меньший размер кода, и некоторые процессоры делают исключение mov, чтобы избежать необходимости в исполнительном модуле для mov. Это было бы для вас более шумным, как, например, возвращение с обратного адреса вашего абонента, который вы могли заметить при пошаговом переходе!

(leave = mov / pop, так что вы можете использовать его даже для меньший размер кода. Это нормально для производительности, в отличие от enter; G CC использует leave в функциях с указателем кадра, который заканчивается RSP, еще не указывающим на сохраненный RBP. Некоторые другие компиляторы предпочитают mov / pop. Но компиляторы обычно используют add rsp, n только тогда, когда они вообще не используют указатель кадра.)

Указатели кадра являются необязательными и не являются обязательной частью компоновки кадра стека. Такие директивы, как .allocstack 16, создают метаданные, которые делают возможным разматывание стека без традиционного связанного списка указателей фреймов.

...