Если исполняемый файл собирается с ключем /SAFESEH, то линкер добавляет в него таблицу с safe exception handlers. В неё помещаюся адреса обработчиков для catch и __except. Поскольку выше хендлер устанавливается вручную, приходится добавлять адрес обработчика в таблицу самостоятельно, что делается компиляцией и линковкой такого asm файла:
?catchguardhandler@cxxrecord@cxxruntime@ntl@@SA?AW4disposition@exception@nt@3@PAUrecord@563@PAUregistration@563@PAUcontext@63@PAUdispatcher_context@563@@Z PROTO SYSCALL
.safeseh ?catchguardhandler@cxxrecord@cxxruntime@ntl@@SA?AW4disposition@exception@nt@3@PAUrecord@563@PAUregistration@563@PAUcontext@63@PAUdispatcher_context@563@@Z
Это всё тоже раболтает без проблем.
Проблемы начинаются, если линкеру указать /OPT:NOICF.
В этом случае в таблицу safeseh помещается реальный адрес catchguardhandler,
однако, код
guard.handler = catchguardhandler;
помещает в guard.handler адрес переходника
jmp catchguardhandler
Соответственно, если при вызове callsettingframe() происходит исключение, диспетчер исключений ОС не находит адрес этого thunk в таблице безопасных обработчиков, и не вызывает его.
Анализ показал, что в объектниках тело catchguardhandler находится всего один раз. Советы делать это свободными функциями, переносить в cpp можно не предлагать — пробовали, не помогает.
Переходник генерируется именно линкером, насколько я понимаю, нужны они для поддержки Edit&Continue. Само Edit&Continue несовместимо с /SAFESEH и отключает его. Однако переходники продолжают генерироваться пока не укажешь /OPT:ICF.
Собственно вопрос, зачем это делает линкер? Версии самые разные, по 2010beta2. Сами Microsoft похоже знают об этой проблеме, поскольку они реализовали вышеприведённые функции на ассемблере. Такой workarround мне не нравится, поскольку неясно, где ещё может всплыть проблема из-за подобных дубликатов (а их море — размер бинарника попросту удваивается). Понятно, что в релизном билде проблемы быть не должно, не понятно, что делать с отладочными.
.
People who are more than casually interested in computers should have at least some idea of what the underlying hardware is like. Otherwise the programs they write will be pretty weird (c) D.Knuth
Не совсем дубликаты функций — речь идёт о переходниках на них.
В общем, уточню вопрос: можно ли пометить произвользую функцию в С++, чтобы на неё не генерировался переходник? Либо получить средствами языка адрес реальной функции.
Собственно, ответы есть, но не совсем подходящие:
1) переходник не генерируется на свободную статическую функцию (но её тогда нельзя пометить для SAFESEH);
2) адрес реальной функции таки можно получить с помощью хака — дизасмом адреса переходника и вычислением реального адреса.
Здравствуйте, Кодт, Вы писали:
К>Наивный вопрос: а зачем реализовывать __try-__except вручную?
Не __try/__except, а try/catch. А вручную потому, что из-под SEH не получить доступа к информации о С++ исключении (в х86).
Здравствуйте, byleas, Вы писали:
B>Не __try/__except, а try/catch. А вручную потому, что из-под SEH не получить доступа к информации о С++ исключении (в х86).
Синтаксис запрещает мешать в одной функции блоки try/catch и __try/__except, но можно сделать вложенный вызов.
где
— sehfilter() проверяет код и информацию SEH-исключения и возвращает EXCEPTION_EXECUTE_HANDLER либо (видимо, тебе это не надо) EXCEPTION_CONTINUE_SEARCH;
— sehhandler() делает что-то полезное наподобие записи в журнал
— cpphandler() может техникой try{throw;}catch/catch/catch уточнить тип исключения и сделать что-то полезное по каждому типу самостоятельно
Кстати! Может быть, тебе вообще необходимо было этот трюк с try-throw сделать? Вместо того, чтобы ковыряться в потрохах механизма исключений?
Здравствуйте, Кодт, Вы писали:
К>Кстати! Может быть, тебе вообще необходимо было этот трюк с try-throw сделать? Вместо того, чтобы ковыряться в потрохах механизма исключений?
Дело в том, что это как раз фрагмент потрохов механизма исключений, выполняет вызов catch блока. И вопрос насчет __try/__except на самом деле довольно сложный, его можно свести к "почему MS так сделал".
В настоящем коде в catchguard есть ещё поля для сохранения некоторого контекста С++ исключения, которые catchguardhandler передаст в хендлер С++ исключений. Последний, в зависимости от типа исключения и ключа /EHa /EHs, либо будет искать следующий catch, либо вызовет __except_handler3.
Предположим, что проблем с рекурсией и контекстом не окажется. Тогда вариант с __try добавляет лишний диспетчерезующий вызов __except_handler3, то есть немного утяжелит С++ RTL. В общем, пока нашел только наивный ответ Для дебаг сборки впринципе не важно, но там проще проверять первый байт catchguardhandler на предмет jmp.
В релизе не хочется лишних оверхедов, хочется обезопасить пользователя от линковки с /OPT:NOICF (хотя смысл такой сборки сомнителен). Идеалный вариант — свалить все на баг в линкере и попытаться его засабмитить. При сборке с Edit&Continue выдается предупреждение, если указать /SAFESEH, и последний отключается. Выдавало бы еще на /OPT:NOICF, и нет проблем
.
People who are more than casually interested in computers should have at least some idea of what the underlying hardware is like. Otherwise the programs they write will be pretty weird (c) D.Knuth
Здравствуйте, gear nuke, Вы писали:
GN>В релизе не хочется лишних оверхедов, хочется обезопасить пользователя от линковки с /OPT:NOICF (хотя смысл такой сборки сомнителен). Идеалный вариант — свалить все на баг в линкере и попытаться его засабмитить. При сборке с Edit&Continue выдается предупреждение, если указать /SAFESEH, и последний отключается. Выдавало бы еще на /OPT:NOICF, и нет проблем
А зачем все эти навороты вообще нужны? Ты чего, собственно, добиться пытаешься?
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Мы добиваемся работоспособности С++ в OS Windows не только в пределах подсистемы Win32. То есть реализуем С++ runtime которому достаточно зависимостей от ntdll.lib или ntoskrnl.lib.
.
People who are more than casually interested in computers should have at least some idea of what the underlying hardware is like. Otherwise the programs they write will be pretty weird (c) D.Knuth
Здравствуйте, Кодт, Вы писали:
К>Кстати! Может быть, тебе вообще необходимо было этот трюк с try-throw сделать? Вместо того, чтобы ковыряться в потрохах механизма исключений?
Мне вообще необходимо _реализовать_ это Точнее, это давно реализовано, просто идёт вылизывание и доработка.