Как именно механизм SEH (структурированная обработка исключений) работает на ARM?

#c #exception #arm #windows-ce

#c #исключение #arm #windows-ce

Вопрос:

Я в курсе общего обзора:http://msdn.microsoft.com/en-us/library/dn743843.aspx ; доступно много информации о механизме исключений в win32 и немного для win64, но ARM не так понятен.

Я хотел бы знать: когда генерируется машинное исключение (нарушение защиты памяти), точно куда передается управление (чтобы я мог установить точку останова)? Переходит ли это в режим ядра? Предположительно, он ищет адрес обработчика из векторной таблицы и передает туда. Выполняет ли код ядра SEH-размотку, или размотка выполняется в пользовательском режиме? Если в пользовательском режиме, то где именно — в части .exe, coreddl или в другом месте?

Предыстория

У нас есть большое приложение, которое размещает обработчики __try / __except на верхнем уровне каждого потока, чтобы информативно регистрировать сбои. Это прекрасно работало в течение многих лет на MIPS. Сейчас мы портируем его на WinCE 7 на ARM, собирая под VS2008. Мы обнаружили, что исключения иногда не обрабатываются должным образом в сборках релизов. Похоже, это зависит от того, где именно в коде генерируется исключение (у меня есть тестовая функция, которая намеренно обращается к нулевому указателю, чтобы вызвать исключение SEH, и я вызываю это из разных мест в коде).

Мы используем опцию /EHsc для включения SEH. В сборках выпуска используется /O2 /Os ; удаление / O2 заставляет его работать, но значительно медленнее. Это наводит меня на мысль, что оптимизатор удаляет что-то, что требуется для работы SEH — но что именно? Не все функции имеют пролог «mov r12, sp …», но некоторые из них все еще не работают должным образом. Это какая-то ошибка в PDATA? Существует ли верхний предел количества записей PDATA? Нормально ли, что выходные данные «dumpbin / pdata» не содержат имени функции в каждой строке?

Используя отладчик (несмотря на выпуск сборки), я могу поместить точку останова в начале моей функции фильтрации исключений (вызываемой из __except) и наблюдать, что она никогда не вводится в случае сбоя. Программа просто завершается.

Ответ №1:

Я не знаком с низкоуровневыми деталями ядра, но указатель на обработчик исключений CRT хранится в dwords непосредственно перед функцией:

 0001106C     DCD _C_specific_handler  ; handler function
00011070     DCD _scope_table         ; scope table (__try/__except map)
00011074 start
00011074     MOV             R12, SP
  

Может быть, поставьте свою точку останова на _C_specific_handler и посмотрите, вызвана ли она. Фактическая функция находится в COREDDLL. Если он вызывается, но не передает исключение в ваш код, возможно, по какой-то причине информация о таблице области действия неверна.

РЕДАКТИРОВАТЬ: вышесказанное применимо к WinCE 6 и более ранним версиям. Поскольку вы упомянули VS2008, я почти уверен, что WinCE 7 по-прежнему использует ту же модель обработки исключений. Упомянутая вами ссылка (на основе кода unwind) относится к новому ядру WinRT, работающему только на ARMv7 (я думаю, что Windows Embedded Compact 2013 также использует его).

Комментарии:

1. Ага! Не могли бы вы указать мне на объяснение таблицы области видимости? У меня также возникли проблемы с определением того, какие фрагменты документации MSDN действительно применимы к WinCE 7.

2. смотрите struct _SCOPE_TABLE в nkarm.h .

3. вот ссылка на MSDN , но она не описывает таблицу области видимости

Ответ №2:

Мы используем опцию /EHsc для включения SEH

Предполагается, что вы используете /EHa с SEH. /EHsc позволяет компилятору оптимизировать всю логику обработки исключений, если он может доказать, что оператор C throw не будет достигнут внутри блока.

Структурированная обработка исключений __try и __except будет работать даже с /EHsc , но тогда C не будет разворачивать стек для структурированных исключений. В результате глобальное состояние может быть несовместимым после исключения, что может привести к сбою вашего обработчика.

Комментарии:

1. Меня не интересуют throw , меня интересуют исключения из памяти (которые являются аппаратными ловушками). Также я полагаю, что / EHa вызывает деструкторы для исключений ловушки процессора, в то время как / EHsc этого не делает?

2. @pjc50: Это верно. Если вы обрабатываете структурированные исключения, которые представляют собой отображение аппаратных ловушек Windows, вам необходимо использовать /EHa , чтобы размотка стека работала правильно. (Если вы пропустите разворачивание стека, ваша программа будет иметь неопределенное поведение.) Моя точка зрения по поводу throw заключается в том, что вы ожидаете, что у вас не будет инструкций throw , и поскольку у вас их нет, компилятор C имеет полное право отбросить всю логику обработки исключений.

3. Поведение без разматывания стека «неопределенно» в смысле C, но прямолинейно: вы пропускаете стек и все, что в нем находится. Но если программа находится в процессе сбоя, то запуск деструкторов с большой вероятностью приведет к повторному сбою, поэтому мы хотим избежать этого. Как я уже сказал, это прекрасно работало на MIPS и win32 в течение многих лет, и работает на ARM тоже — если не включена оптимизация.

4. (В частности, код внутри скобок __except() запускается перед развертыванием стека, который мы используем для регистрации трассировки стека)

5. @pjc50: Вы пробовали с /EHsc ? __except фильтры все равно будут запускаться перед развертыванием стека; только фильтры, расположенные глубже в стеке, будут запускаться первыми, и это может быть важно. Вы не можете войти в систему, например, пока в вашем файле журнала удерживается блокировка.

Ответ №3:

Возможно, это не точный ответ на ваш вопрос, но если ваша цель — перехватывать критические аппаратные исключения и регистрировать их стек вызовов, то начиная с Windowce CE 6.0 вы можете использовать AddVectoredExceptionHandler. В приложении, над которым я работаю (vs2005, только ARM), я могу получать стеки вызовов с помощью этой функции, также вам не нужно добавлять SEH catch / throw, обработчик исключений всегда будет вызван.

Ответ №4:

Это очень просто. Введите «ARM ARM» в вашей любимой поисковой системе.

Более узкий поиск «исключение предварительной выборки ARM».

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

На других платформах могут быть процессоры advanced Memory Management Unit (MMU), которые могут иметь ограждения. Когда программа обращается за пределы огороженной области, генерируется выполнение или прерывание. Область будет запрограммирована в MMU. Это позволяет ОС защищать области памяти, к которым программа пользователя не должна обращаться, даже если память существует.

Доступ к неопределенным областям памяти, к которым у вашей программы нет доступа, определяется как «неопределенное поведение». Это поведение может варьироваться в зависимости от компилятора или платформы, и стандартного поведения не существует. Некоторые платформы могут генерировать «сигналы», другие могут генерировать «исключения», некоторые передают сообщения, другие просто вылетают. Если вы хотите переносную обработку исключений в памяти, вам нужно будет написать код, зависящий от платформы.

Комментарии:

1. Я хотел бы описать поведение, которое зависит «от операционной системы»; Я знаком с общим фоном.