Пользователь
Информация
- В рейтинге
- 1 880-й
- Откуда
- Петропавловск, Северо-Казахстанская обл., Казахстан
- Зарегистрирован
- Активность
Специализация
Software Developer, Embedded Software Engineer
Pure C
Assembler
X86 asm
Win32 API
Visual Basic
MySQL
Git
OOP
Electronics Development
Reverse development
Поправлю некоторые опечатки, а то пост уже не отредактировать:
даже если он не перемещаемый.
умеет делать и так, и так.
Но если вы собираетесь свой PE-файл запускать под Win32s (это 32-битная подсистема для 16-битных Windows 3.1/3.11), образ обязательно должен быть перемещаемым и иметь релоки.
Нет, не «так как ценился размер файла», а так как у Microsoft-овского линкера поведением по-умолчанию было подразумевать ключ /FIXED (означающий «сгенерировать неперемещаемый образ»), если генерируется EXE-файл, а не DLL.
Такая тактика даже закреплена в официальной MS-овской спецификации на форматы COFF и PE:

А вообще, читайте большой коммент. Ваш спор (zvszvs и Angeld) со стороны выглядит странно: вроде бы оба что-то знают на тему PE/COFF, но у обоих в голове каша — даже не только не уровне знаний, сколько на уровне подхода к самому спору.
Нет такого бита в «свойствах секции». Это бит relocs stripped поля Characteristics из NT-заголовка.
Дальнейшее ваше обсуждение с Angeld тоже расстраивает: оба не в состоянии разделить компилятор и линкер и спорите с изначально неправильным подходом к спору и к определению истины.
Ваш спор о том «по умолчанию имеют ли EXE фиксированную базу» лишён смысла в первую очередь потому, что он построен так, как будто существует какой-то общемировой догмат/стандарт/подход о способе формирования EXE, и в его рамках есть своё «по-умолчанию». А его нет.
Во-первых, будет PE-файл иметь возможность загружаться по произвольной базе или будет иметь фиксированную базу зависит не от компилятора, а от линкера. Линкер, а не компиятор формирует основообразующие структуры PE-файла. Линкер, а не компилятор, определяет, будет ли в поле Characteristics стоять флаг IMAGE_FILE_RELOCS_STRIPPED. Линкер, а не компилятор, формирует таблицу релокаций. Компилятор определяет только набор секций, и начинку и характеристики, а линкер склеивает начинку всех одноимённых секций всех входных объектных файлов, комбинирует характеристики секций, формирует окончательную таблицу секций, разрешает зависимости/проставляет адреса, формирует прочие структуры PE-файла и даёт на выходе готовый исполняемый файл. Поэтому нужно спорить не «имеют ли EXE-файлы релокации вообще в принципе» и не «делают ли компиляторы EXE-файлы перемещаемыми или нет», а нужно говорить о линкере.
Во-вторых, линкер — он не как бог в монотеических религиях. Нет единого истинного линкера со своим линкерским догматом, всё кроме которого считалось бы ересью. Очевидно, что различных линкеров существует очень большое число. Многие из них вообще не имеют никакого отношения к генерации PE-файлов и миру Windows соответственно: те же линкеры из *nix-мира, формирующие ELF-бинарники, или вообще линкеры, используемые при сборке прошивок для микроконтроллеров.
Но даже если взять из всего множества линкеров те, что заточены на формирование исполняемых бинарников под Windows в формате PE, то и таких линкеров много. Есть линкер от Microsoft, который идёт в составе Visual C++ и DDK/WDK/Platform SDK. Который на вход принимает объектные файлы формата COFF. Есть линкер от Borland, который шёл в составе Delphi и C++ Builder, который на вход принимает объектные файлы формата OMF, кстати говоря. Есть линкер ulink от Юрия Харона, который в своих статьях постоянно восхвалял Крис Касперски. Есть, надо полагать, ряд линкеров из никс-мира, способных при желании генерировать PE-бинарники из объектных файлов формата ELF — в рамках кросс-компиляции/кросс-сборки. Есть линкеры из продуктов менее известных, из какого-нибудь Lazarus-а, например. Есть, наконец, самодельные/экспериментальные линкеры.
И все эти линкеры ведут себя по разному. Более того: «линкер от Microsoft» (или любой другой из выше упомянутых) — это не один единственный инструмент из палаты мер и весов. Это целый зоопарк версий одного и того же инструмента. И одна версия может вести себя так, а другая версия уже иначе.
Так о чём вы спорите, господа?

Ваш спор имел бы какой-то смысл, если бы вы конкретно говорили: «линкер от Microsoft такой-то версии по-умолчанию делает EXE-файлы неперемещаемыми». Тогда был бы предмет спора, была бы правая и неправая сторона. Можно было бы поставить эксперимент, и можно было бы пойти и ткнуть кого-то носом в документацию. Или вообще, если говорить о способах получения EXE какими-то инструментами, вообще не предполагающими использование линкера (например каким-нибудь FASM-ом) — то так и нужно ставить вопрос «когда FASM генерирует EXE сразу из ассемблерного исходника, минуя стадию формирования объектных файлов и линковку, делает ли он образ перемещаемым по умолчанию».
Ещё раз: будет ли в PE-файле присутствовать таблица релокаций, или же её не будет, а зато будет установлен флаг IMAGE_FILE_RELOCS_STRIPPED — зависит от линкера. И в этом плане способности линкеров и тактика по умолчанию может отличаться от линкера к линкеру. Microsoft-овский линкер умееть делать и так так.
Ключ /FIXED заставить его не делать таблицу релокаций. Ключ /FIXED:NO заставит его сделать образ перемещаемым. Долгое время для Microsoft-овского линкера тактикой по умолчанию было подразумевание /FIXED для EXE, и /FIXED:NO для DLL/OCX/SYS и всего остального. Оно и сейчас позиционируется так же:
/FIXED:NO is the default setting for a DLL, and /FIXED is the default setting for any other project type.
В какой-то момент был добавлен ключик /DYNAMICBASE для ASLR, который подразумевает /FIXED:NO.
В третьих, вы может быть спорите не насчёт того, формируют ли инструменты сборки EXE-файл с или без таблицы релокаций, а спорите о том, требует ли Windows от EXE-файлов наличие перемещаемости? Опять же, вопрос не имеет смысла без уточнения версии ОС. Большинству версий ОС непосредственно на наличие перемещаемости наплевать: и не только для EXE, но и для DLL. Если участок виртуального адресного пространства процесса, в которое PE-образ хочет (в соответствии со своим полем ImageBase) быть спроецирован, свободен, а сторона, инициировавшая загрузку PE-образа, не потребовала сделать загрузку по нестандартной базе — PE-образ (хоть EXE, хоть DLL) будет загружен, даже если он перемещаемый. Если PE-образ не может быть загружен туда, куда он хочет быть загруженным, а должен быть загружен по непривычной для него базе — либо потому что «родному» базовому адресу он не может быть загружен, так как это участок виртуального АП чем-то занят, либо потому что сторона, инициировавшая загрузку, потребовала загрузить образ по указанному адресу (MapViewOfFileEx имеет такую опцию) — тогда, если он не перемещаемый, загрузка PE-файла не удастся (будь-то хоть EXE, хоть DLL).
Но если вы собираетесь свой PE-файл запускать под Win32s (это 32-битная подсистема для 16-битных Windows 3.1/3.11). Поэтому я не зря выше сказал, что нужно уточнять версию ОС. После появления технологии ASLR, очевидно, жёсткого требования, что все бинарники должны быть перемещаемыми, не появилось — это бы уничтожило обратную совместимость. Поэтому если ASLR включен, а PE-файл не предполагает перемещаемости, он будет загружен так, как если бы ASLR не действовало.
В-четвёртых, когда вы ниже говорите «компиляторы (не) генерирует релокации» и спорите на эту тему, это не просто маленькая оговорка, которую можно оправдать фразой «да ладно, все же прекрасно поняли, что мы имеем в виду линкеры, а не компиляторы». Это пускание рассуждений по ложному пути, потому что при желании к этим словам можно прицепиться.
Потому что на самом деле в рамках PE\COFF существует два разных типов сущностей, чуть-чуть близких друг к другу по назначению, но всё-таки сильно разных — и обе называются релоками/релокациями.
И это не одно и то же. Это два разных типа сущностей: за ними стоят разные структуры данных, разные назначения и отличающиеся константы. Использование одинакового или почти одинакового названия способно вызывать у людей путаницу; и когда читаешь подобные диспуты, со стороны даже не понятно, то ли спорящие сами путают понятия, то ли пытаются запутать оппонента.
Первое — это relocations в COFF-файлах (объектных файлах, OBJ-файлах). Этот тип информации не имеет никакого отношения к загрузке исполняемых файлов по нестандартному адресу. Как и PE-файл (который являет собой химеру из MZ- и COFF форматов), COFF-файл имеет внутри себя таблицу секций и сами секции. Это почти такие же секции, как в испоняемом PE-файла (как минимум таблица секций в COFF имеет абсолютно тот же формат/структуру записей — IMAGE_SECTION_HEADER), но это как правило большое количество довольно мелки секций, которые на этапе линковке будут перегруппированы, часть секций будет отброшено, а оставшиеся одноимённые секции будут склеены.
Так вот у каждой секции COFF-файла есть собственная таблица релокаций (на неё и её размер указывают поля PointerToRelocations и NumberOfRelocations структуры IMAGE_SECTION_HEADER). Записями/элементами этой таблицы являются тройки вида {адрес_в_рамках_секции; тип_релокации; индекс_элемента_таблицы_символов} — и эта таблица является ни чем иным, как таблицей ссылок на внешние сущности/символы, с той лишь оговоркой, что ссылки вполне себе могут быть и не на внешние символы. Это ключевая структура для осуществления процесса линковки: именно пользуясь таблицей релокаций (точнее таблицами релокаций — у каждой секции COFF-файла она собственная) и таблицей символов линкер занимается тем, что во всех местах, где в коде/данных результирующего файла должна быть ссылка (указатель, адрес, относительное смещение) на какую-то сущность из другого COFF-файла (или даже из того же самого), он правильным образом проставляет эти указатели/адреса/относительные смещения.
В этом ключе каждая «релокация» описывается структурой IMAGE_RELOCATION размером 10 байт (не кратно 4, да):
Первое поле содержит смещение места (в рамках COFF-секции) от начала COFF-секции. Второе поле содержит ссылку (в виде индекса) на элемент таблицы COFF-символов, описывающий сущность, на которую в данном месте секции после окончания линковки должна оказаться корректная ссылка того или иного вида. Третье поле содержит тип релокаций — по скольку «ссылки», вшитые в код или данные, могут быть разного типа. На ум приходит, что как минимум это могут быть абсолютные и относительные адреса (то есть уже два типа ссылок), в реальности же форматом COFF предусмотрено гораздо боле вариантов, причём для каждой процессорной архитектуры (I386, MIPS, ALPHA, PowerPC, ARM, IA64) набор типов свой.
Этот тип релоков формируется компилятором и формируется ВСЕГДА. Поэтому постановка вопросов «формирует ли компилятор релоки» не такой уж невинный, и откупиться фразой «ой, да ладно, мы под компилятором имели в виду совокупность из компилятора и линкера вообще и линкер в частности» не получится.
Этот тип информации о релокациях используется при линковке (он является абсолютно ключевым для процесса линковки), но дальше процесса линковки он не идёт. В сформированном PE-файле в таблице секций поля PointerToRelocations и NumberOfRelocations всегда будут занулены — в PE-файла нет ни места таблицам релокаций, ни смысла для их существования.
Тем не менее, релокации этого рода влияют на формирование релокаций второго года.
Второй тип информации, который тоже называется релокациями — это информация совершенно иного рода и формата. Если первый тип присутствует только в объектных файлах, и никогда не присутствует в исполняемых файлах, то второй тип, наоборот, может присутствовать в исполняемых файлах, но никогда не присутствует в объектных файлах.
Это по сути, перечень всех мест в готовом исполняемом файле, которые нужно подправить (adjust), если образ будет проецироваться по нестандартной базе.
Если в COFF-файлах таблиц релокаций много (у каждой секции своя собственная — на неё указывается IMAGE_SECTION_HEADER::PointerToRelocations ), то в PE-файле таблица релокаций, если и есть, то одна — и указывает на неё директория релокаций (OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC]).
Если в COFF-файлах таблица релокаций была гомогенной таблицей 10-байтовых записей с тремя полями (троек: {смещение; индекс_символа; тип_релока}), то в PE-файлах таблица релокаций представляет собой блочный список записей вида {тип_релока; смещение}: каждый блок описывает область образа размером 4 Кб и содержит переменное число двухбайтовых (а не 10-байтовых, как в COFF) структур вида {тип_релока; смещение} — смещение в данном случае отсчитывается от начала 4К-страницы, описываемой данным блоком, а в качестве типов релокаций используется совершенно другой (по сравнению с релокациями в COFF) набор констант:
И наконец третий тип информации, связанный с релокациями второго типа, который тоже формируется линкером и тоже на основе релокаций первого типа из COFF-файлов: это таблцица FIXUP-ов, которая попадает в debug-директорию PE-файла или в отдельный DBG-файл, если отладочная информация из PE-файла извлекается+изымается. В то время как в PE-формате есть таблица директорий, и одна из директорий типа IMAGE_DIRECTORY_ENTRY_DEBUG описывает блок отладочной информации, внутри отладочной информации есть своя таблица директорий — таблица отладочных директорий. В этой таблице отладочных директорий может присутствовать директория IMAGE_DEBUG_TYPE_FIXUP, описывающая таблицу фиксапов.
Фиксапы — это тройки вида {тип; rva_ссылки; rva_места_куда_ссылаются}. Эта таблица перечисляет все места в готовом исполняемом бинарнике, где применена абсолютная адресация. Как не трудно догадаться, она сформирована на основе COFF-релокаций из COFF-файлов (но не только на её основе!), подобно тому, как таблица base relocations в PE-файле сформирована линкером на основе COFF-релокаций
Она похожа таблицу COFF-релокаций в том плане, что это тоже гомогенный набор структур с тремя полями. Она похожа на таблицу base-релокаций PE-файла тем, что как и таблица base-релокаций, fixup-таблица одна на весь образ (а таблица COFF-релокаций у каждой секции своя). В отличие от COFF-релокаций, хоть и тут и там мы имеем набор структур с тремя полями, в fixup-таблице нет никаких ссылок на таблицу символов (хотя таблица символов является частью отладочной информации и таблица fixup-ов тоже является частью отладочной информации). В отличие от таблицы base-релокаций в таблице fixup-ов перечислены не только места, где применена абсолютная адресация и которые требуют корректировке при загрузке не по предпочтительной базе, но и места, где применена RVA-адресация (а она применена в таблицах импорта/экспорта/ресурсов). Места с RVA-адресацией не перечислены в base-релокациях PE-файла: при загрузке по нестандартной базе такие места корректировать не нужны, но таблица fixup-ов в составе отладочной информации решает немного другую задачу: обеспечение хот-патч-абильности образа, судя по всему. В отличие от таблицы COFF-релокаций, в таблице fixup-ов не перечислены места, где используется относительная адресация (call/jmp/jcc-инструкции). Тем не менее, как и таблица COFF-релокаций, таблица fixup-ов использует тот же набор констант для поля «тип».
Есть рассекреченные в рамках суда по делу Iowa vs. microsoft (или же слитые?) внутрикорпоративные переписки с участием Билла Гейтса и тимлидов проектов.
Читая, понимаешь, что они там усердно бились ради увеличения скорости работы/загрузки на каждую долю секунду. 1997 год. Как сейчас — большой вопрос.
Тем не менее, это более достойный и правильный путь противостоять тому, что творится с современной Windows, чем просто кричать «переходите на ллинукс».![]()
Пишите на коленке утилиту, которая вычисляет ключ шифрования, который путём xor-ирования сохранённых данных из /dev/random «расшифровывает» их в текст конституции, например.
Я ответил в ЛС.
Когда я вижу или слышу «куар», я сатанею от раздражения. Господа хорошие. Есть латинская транскрипция букв латинского алфавита. Её вы слышали в школе на уроках геометрии и химии. Треугольник а—бэ—цэ, цэ-два—аш-пять—о-аш.
Есть английская традиции транслитерации: си-ди диск, ар-джи-би.
Я что-то никогда раньше не слышал про «треугольник «эй-бэ-цэ», про «цэ-ди диски или «си-дэ диски»», про «си два эйч пять оу аш». Вы либо используете одну форму транскрипции, и тогда у вас «ку эр», либо другую, и тогда у вас «кью ар».
У вас либо цэ-пэ-у, либо си-пи-ю. Так какого чёрта возник и в головах людей закрепился этот уродец «куар»?
Я вас умоляю. Дельфины это совершенно безобидная история. Тут у нас всем городом активно собирают 15 000 долларов на то, чтобы свозить ребёнка в Грузию, где ему должны вылечить аутизм путём пересадки клеток из бедра в головной мозг. Так прямо и написано в посте, который все без раздумий репостят, чтобы поскорее собрать всю необходимую сумму.
Эти люди, кажется, готовы собирать деньги на лечение аутизма с помощью гвоздей, выдержанных в моче африканского осла — плевать что ноль доказательств не только эффективности, но и безопасности. Зато на предложение вакцинировать детей они вас будут буквально порвут словно разъярённый зверь. Как вы смеете непроверенную вакцину... деткам... и вообще билгейц чипирует!
А можно выдержку из стандарта, где зафиксирована легитимность такого вывода?
Правило же должно звучать не как «не вызывать виртуальные функции», а «вызывая виртуальные функции, не мечтать о вызове непонятно чего, а отдавать себе отчёт, что может быть вызвано, а что нет».
Sleep(1) «помог бы» (по вашим словам), но Sleep(1) сделает так, что то, что мега-фукнция вычислила бы за секунду, она будет вычислять за полторы минуты.
Единственное вразумительное объяснение, которое я могу дать вашим наблюдением вот такое: у вас на машине, где вы это наблюдали, была катастрофическая ситуация с системой охлаждения. Высохшая/неправильно нанесённая термопаста, например, или что-то в таком духе.
Как только вы запускали поток с бесконечным циклом, он, в норме не страшный для процессора, вызывал его критический разогрев до точки, где начинал действовать throttling для предотвращения перегрева. Естественно, что throttling вызывал деградацию всей системы.
В норме того, что вы описываете, не происходит.
Речь шла совершенно о другом.
Недавно тут была статья «Как понять, чо вы пишите не на C++, а на Си с классами».
Так вот, если вы пореверсите продукты MS, написанные на C++, да хоть тот же обсуждаемый здесь VB6, то откроете для себя, что внутри MS для своих же продуктов С++ используется как «Си с классами».
Они не используют STL или сишный механизм исключений внутри своих продуктов. Не на уровне API ОС, не между разными DLL-библиотеками, а внутри одной программы/библиотеки они это обходят стороной.
Ну так и файл, открытый родными средствами средствами VB, не будет закрыт при выходе из процедеры. И оконо, показанное из процедуры, тоже не будет закрыто.
А если обернуть в класс хоть получение хендла через WinAPI, хоть открытие файла родными средствами — тогда экземпляр класса будет правильным образом уничтожен (если других ссылок нет).
В общем, деланье чего-то через WinAPI тут приведено зря, потому что в связанных с этим проблемах оно ничем не отличается от октрытия файлов родными средствами.
Что за управляющие вызов виртуальной машины? Имплементации P-кодных инструкций не экспортируются — вызвать их нельзя. Да и функциями эти имплементации не являются.
Из управляющих можно назвать разве что ProcCallEngline/ProcMethEngline — эдакие гейты между native-кодом и P-кодом — позволяют вызывать P-код из native-среды.
Но не представляю, каким образом их использование совместно с goto может нести какие-то проблемы.
Кто говорил о кросс-процессности?
"нтерпретация байт-кода" для режима P-CODE, потому что в таком режиме библиотека машины все равно интерпретирует его по ходу выполнения
Тогда скомпилированные Java-программы работают путём интерпретации Java-байкода, дотнетовские программы работают путём интерпретации MSIL-байткода. Да и процессоры интерпретируют машинный код.
Можно было бы говорить «в отличие от режима компиляции в Native», если бы суть обработки P-кодных инструкций заключалась бы в том, что виртуальная машина P-код переписывала бы в Native-код (генерируя для каждой пи-кодной инструкции дюжину машинных), а потом отдавала бы сгенерированный Native-код на выполнению процессору.
Но она же не так делает. В том большом комменте описано, как она это делает.
Коротко и упрощённо говоря: для каждой P-кодной инструкции есть свой хендлер, опкод P-кодной инструкции является индексом в таблице указателей на соответствующие обработчики.
Приведённое выше академическое определение понятия «интерпретор» настаивает на том, что входными данными для него является исходный текст программы, а обработка происходит построчно.
В данном случае нет ни построчности, ни исходного текста в роли входной последовательности для виртуальной машины.
В теории можно было бы вообще создать архитектуру процессора, которая бы машинно умела исполнять VB-шный P-код. Ну какая же это интерпретация?
Да что же в этом дорогого? Это не было дорогим удовольствием 25 лет назад, а уж тем более сейчас.
Это не дороже, чем две лишних локальных переменных.
Я смотрю, пока я писал, вы перекроили свой коммент.
Как понять «незакрытый вызов WinAPI»? Вызовы WinAPI были не хаком, а штатной возможностью, точнее даже были осуществимы через два разных механизма: через Declare Sub/Function и через TLB.
Процедуры генерировались в чистейший машинный код.
Давайте разделять понятия: виртуальная машина — это то, что, принимая на вход цепочку команд, каким-то образом выполняет их, предпринимая какие-то действия по поводу каждой команды и меняя своё внутреннее состояние.
Реализация виртуальной машины VB-шного P-кода жила в MSVBVMxx.DLL (что и давало название этой библиотеки), но помимо виртуальной машины значительную часть библиотеки составляло то, что можно назвать «стандартной библиотекой» по аналогии с языками Си/Си++. Т.е. это просто различные «встроенные функции», заявленные как «часть языка», а также служебные функции (в случае C++ примером такой функции может стать _purecall). Это обычные процедуры в виде машинного кода, они к виртуальной машине отношения не имеют.
Так вот, то, что генерировалось в режиме создания Native-кодных исполняемых файлов, представляло собой обычный машинный код x86 и не с вкраплениями P-кодных кусочков, а с вкраплениями вызовов служебны функций для выполнения тех задач, которые приходится очень часто решать и которые раздули бы объём кода, если бы эти задачи инлайнились.
Например — копирование структур. Копирование структур можно было бы «инлайнить», сгенерировав код для копирования каждого члена структуры (в общем случае структуру нельзя копировать банальными memcpy, ведь там могут быть указатели на строки или COM-интерфейсы, массивы — и копировать это надо по умному). Вместо генерировалась одна единственная call-инструкция, вызывающая функцию рантайма, которая брала на себя все заботы по правильному копированию структуры. Ей передавался некий дескриптор структуры, предопределявший правила копирования.
Или же после каждого вызова метода COM-интерфейса (читай «метода вызова любого VB-объекта или любого внешнего COM-объекта»), который мог выбросить ошибку путём возврата HRESULT, ставился вызов __vbaHresultCheck, который брал на себя всю работу по «транслированию» кода сбоя (HRESULT) в VB-ошибку (VB использовал SEH-исключения для работы своего механизма ошибок).
Плохо читали — ничего там не упущено. Оно скрыто под спойлером «Немного подробностей о том, как происходит компиляция в Native-код», внутри которого есть свой под-спойлер «Интересный вопрос касательно IL на входе бэкенда C2».
В Википедии приводится вот такое определения термина «интерпретация» в контексте ЯП:
Интерпрета́ция — построчный анализ, обработка и выполнение исходного кода программы или запроса, в отличие от компиляции, где весь текст программы, перед запуском анализируется и транслируется в машинный или байт-код без её выполнения[4][5][6]
Источниками такого определения назван не Вася Пупкин, а литература, написанная авторами, заслуживающими уважения:
Так что в соответствтии с общеприянтым определением это не интерпретация, а компиляция и исполнение байт-кода виртуальной машины. Видимо многим просто нелегко принять факт, что презираемый очень многими инструмент с репутацией «для чайников в программирование» имел крутейший и непревзойдённый механизм патчинга скомпилированного байт-кода по живому. «Ну не может же такого быть, что оно вот так легко на лету перекомпилирует и перекраивает скомпилированную процедуру, в данный момент исполнявшуюся — значит там под капотом банальная интерпретация» — так видимо рассуждало большинство.
SEH-фрейм занимает
8 байт2 sizeof(void) на стеке — разве это дорого? В обходе односвязного списка и вызове обработчика по указанному адресу тоже вряд ли что-то дорогое можно узреть. Скорее всего по сравнению с тем, что обработчики будут делать, overhead от обхода цепочки вызовов будет незначительным.SEH был дорогим по совсем другой причине. Вызов RaiseException приводит к переходу в режим ядра, а это довольно медлительная вещь. В этом смысле SEH-исключения не могли стать альтернативой для кодов возрата статуса и должны были использоваться только для реально исключительны ситуаций. Попытка переделать цикл, проходящийся по миллиону элементов и вызывающий для каждого элемента какую-нибудь функцию, которая с вероятностью 30...50% может сбойнуть на выкидывания исключений очень значительно замедлила бы работу такого цикла, потому что 500 000 раз выкидывать исключение — это 500 переходов в режим ядра и обратно, что на порядок или несколько порядков медленнее, чем если бы вызываемая функция просто возвращала код ошибки, при условии что в в вызываемой функции только какая-нибудь арифметика и манипуляция данными и нет системных вызовов.