Почему не может 64-разрядная Windows разворачивать стек во время исключения, если стек пересекает границу ядра - когда 32-битная Windows может?
Контекст всего этого вопроса исходит из:
Случай исчезновения исключения OnLoad - исключения обратного вызова пользовательского режима в x64
Фон
В 32-разрядной Windows, если я выкидываю исключение в моем коде режима пользователя, который был вызван из кода режима ядра, который был вызван из моего кода режима пользователя, например:
User mode Kernel Mode
------------------ -------------------
CreateWindow(...); ------> NtCreateWindow(...)
|
WindowProc <---------------------+
Structured Exception Handling (SEH) в Windows может разматывать стек, отматывать обратно через режим ядра, обратно в мой код пользователя, где я могу обрабатывать исключение, и я вижу допустимую трассировку стека.
Но не в 64-битной Windows
64-разрядные версии Windows не могут этого сделать:
По сложным причинам мы не можем распространять исключение в 64-разрядных операционных системах (amd64 и IA64). Это было так с первой 64-разрядной версии Server 2003. На x86 это не так - исключение распространяется через границу ядра и заканчивается тем, что обращается назад назад.
И так как в этом случае нет способа найти надежную трассировку стека, нужно было принять решение: пусть вы увидите ненужное исключение или вообще скройте его:
Архитекторы ядра на тот момент решили принять консервативный подход, основанный на AppCompat - скрыть исключение и надеяться на лучшее.
В статье рассказывается о том, как это было во всех 64-битных операционных системах Windows:
- 64-разрядная версия Windows XP
- 64-разрядная версия Windows Server 2003
- 64-разрядная версия Windows Vistali >
- 64-разрядная версия Windows Server 2008
Но начиная с Windows 7 (и Windows Server 2008) архитекторы передумали - вроде. Для 64-разрядных приложений только (не 32-разрядных приложений) они (по умолчанию) останавливают, подавляя эти исключения пользовательского ядра. Итак, по умолчанию, на:
- 64-разрядная версия Windows 7
- Windows Server 2008
все 64-разрядные приложения будут видеть эти исключения, где они никогда не видели их.
В Windows 7, когда происходит сбой приложения native x64 таким образом, уведомляется помощник по совместимости программ. Если приложение не имеет Windows 7 Manifest, мы показываем диалоговое окно с сообщением о том, что PCA применила прокси-совместимость приложений. Что это значит? Это означает, что при следующем запуске приложения Windows будет эмулировать поведение Server 2003 и исключить исключение. Имейте в виду, что PCA не существует на сервере 2008 R2, поэтому этот совет не применяется.
Итак, вопрос
Вопрос в том, почему 64-разрядная Windows не может отключить стек обратно через переход ядра, а 32-разрядные версии Windows могут?
Единственный намек:
По сложным причинам мы не можем распространять исключение в 64-разрядных операционных системах (amd64 и IA64).
Подсказка усложняется.
Я не могу понять объяснение, так как я не разработчик операционной системы, но я хотел бы понять, почему.
Обновление: исправление, чтобы остановить подавление 32-разрядных приложений
Microsoft выпустила исправление, позволяющее использовать 32-разрядные приложения, также больше не имеет исключений:
KB976038: Исключения, которые выбрасываются из приложения, работающего в 64-разрядной версии Windows, игнорируются
- Исключение, которое выбрано в процедуре обратного вызова, выполняется в пользовательском режиме.
В этом случае это исключение не приводит к сбою приложения. Вместо этого приложение переходит в несогласованное состояние. Затем приложение выдает другое исключение и сбой.
Функция обратного вызова в режиме пользователя обычно является функцией, определяемой приложением, которая вызывается компонентом режима ядра. Примерами функций обратного вызова пользовательского режима являются процедуры Windows и процедуры перехвата. Эти функции вызывают Windows для обработки сообщений Windows или для обработки событий Windows.
Это исправление позволяет вам отключить Windows от использования исключений по всему миру:
HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options DisableUserModeCallbackFilter: DWORD = 1
или для каждого приложения:
HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\Notepad.exe DisableUserModeCallbackFilter: DWORD = 1
Поведение также было зарегистрировано на XP и Server 2003 в KB973460:
Подсказка
Я нашел еще один намек при исследовании с использованием xperf для захвата трассировки стека в 64-битной Windows:
Прохождение стека в Xperf
Отключить пейджинговый исполнитель
Чтобы трассировка работала на 64-битной Windows, вам нужно установить ключ реестра DisablePagingExecutive. Это говорит операционной системе, чтобы не нажимать на драйверы режима ядра и системный код на диск, что является предпосылкой для получения 64-битных стеков вызовов с использованием xperf, поскольку ходьба по 64-битным стекам зависит от метаданных в исполняемых изображениях, а в некоторых ситуациях xperf код стека стека не разрешается касаться выгружаемых страниц. Выполнение следующей команды из командной строки с повышенными правами приведет к установке этого раздела реестра.
REG ADD "HKLM\System\CurrentControlSet\Control\Session Manager\Memory Management" -v DisablePagingExecutive -d 0x1 -t REG_DWORD -f
После установки этого раздела реестра вам необходимо перезагрузить систему, прежде чем вы сможете записывать стеки вызовов. Наличие этого флага означает, что ядро Windows блокирует больше страниц в ОЗУ, поэтому это, вероятно, будет потреблять около 10 МБ дополнительной физической памяти.
Это создает впечатление, что в 64-битной Windows (и только в 64-разрядной Windows) вам не разрешается ходить с ядрами, потому что на диске могут быть страницы.