Какова цель функции _chkstk()?

Недавно я использовал /FAsu вариант компилятора Visual С++, чтобы вывести сборку источника + с особенно длинным определением функции участника. На выходе сборки после установки фрейма стека существует один вызов таинственной функции _chkstk().

Страница MSDN на _chkstk() не объясняет причину, по которой эта функция вызывается. Я также видел вопрос о переполнении стека Выделение буфера большего размера страницы в стеке приведет к повреждению памяти?, но я не понимаю, что OP и принятый ответ говоря о.

Какова цель функции _chkstk() CRT? Что он делает?

Ответ 1

Страницы Windows в дополнительном стеке для вашего потока, поскольку он используется. В конце стека есть одна страница защиты, отображаемая как недоступная память - если программа обращается к ней (поскольку она пытается использовать больше стека, чем в настоящее время отображается), есть нарушение доступа. ОС улавливает ошибку, отображает на другой странице стека по тому же адресу, что и старая страница защиты, создает новую защитную страницу, расположенную за пределами старой, и возобновляет работу с инструкцией, вызвавшей нарушение.

Если функция имеет более одной страницы локальных переменных, то первый адрес, к которому он обращается, может быть более одной страницы за пределами текущего конца стека. Следовательно, он пропустит страницу защиты и вызовет нарушение доступа, которое ОС не осознает, потому что требуется больше стека. Если требуемый общий стек особенно огромен, он может даже выйти за пределы защитной страницы, за пределами пространства виртуального адреса, назначенного стеку, и в память, которая фактически используется для чего-то еще.

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

Ответ 2

Я просмотрел код для __chkstk, и он выполняет повторные проверки стека с интервалами в одну страницу. Таким образом, он не должен делать никаких вызовов в ОС. Параметр rax - это размер данных, которые вы хотите добавить. Это гарантирует, что целевой адрес (текущий rsp - rax) доступен. Если rax > rsp, он делает это для адреса 0. В качестве интересного ярлыка он сначала сравнивает адрес с gs:[10h], который является текущей самой низкой страницей, которая отображается; если целевой адреs >= это, то он ничего не делает.

Кстати, для 64-битного кода, по крайней мере, написано два символа подчеркивания: __chkstk__.