Соглашения о вызовах
Прежде чем перейти к другим инструкциям, сделаем краткий обзор соглашений о вызовах. Несколько инструкций, представленных в предыдущем разделе, могут обеспечить первоклассную отладку. Однако, чтобы показать, как нужно расшифровывать окно Disassembly, необходимо связать воедино процедуру вызова и соглашения о вызовах.
Соглашение о вызовах указывает, как нужно пересылать параметры в функцию и как происходит очистка стека, когда выполняется возврат из функции. Соглашение о вызовах диктует программист, который кодирует функцию. Этому соглашению должен следовать каждый, кто вызывает эту функцию. CPU не диктует каких-либо специальных соглашений о вызовах. Понимание этих соглашений облегчает отыскание параметров в окне Memory и определение потока операций языка ассемблера в окне Disassembly.
Существует всего пять соглашений о вызовах, но только три из них — наиболее общие: стандартный вызов (__stdcall), С-декларация (__cdecl) и this-вызов. Стандартный вызов и С-декларацию программист может указывать самостоятельно, а this-вызов применяется в С-программе автоматически, когда он задает способ передачи этого указателя. Два других соглашения о вызовах — это быстрый вызов (_fastcalll) и так называемые "голые" (naked) соглашения о вызовах. По умолчанию операционные системы Win32 не используют соглашение быстрого вызова в коде пользовательского режима, потому что оно не переносимо на другие CPU. Соглашение о "голых" вызовах применяется для программирования драйверов виртуальных устройств (VxD) и в тех случаях, когда программист хочет самостоятельно управлять прологом и эпилогом (об этом будет рассказано в главах, 12 к 14).
В табл. 6.3 перечисляются все соглашения о вызовах. Вспомним описание схемы декорирования1 имен для установки точек прерывания на системных функциях из главы 5. Из табл. 6.3 ясно, что схему декорирования имен диктует соглашение о вызовах.
"Декорированное" имя в C++ — это генерируемая компилятором строка, содержащая, кроме собственно имени, символы, используемые компилятором или компоновщиком для получения информации о типе.
— Пер.
Читатель, никогда не встречавшийся с соглашениями о вызовах, может задаться вопросом: почему существуют различные их типы?. Различия между вызовами _cdecl и _stdcall довольно тонкие. При стандартном вызове вызываемая функция очищает стек, поэтому она должна точно "знать" количество ожидаемых параметров. В связи с этим функция стандартного вызова не может иметь переменного числа аргументов (как, например, printf). Поскольку для функций _cdeci стек очищает вызывающая программа, функции с переменным числом аргументов допустимы. Стандартный вызов используется по умолчанию для системных функций Win32, а также для функций языка Visual Basic.
Таблица 6.3. Соглашения о вызовах
Соглашение о вызове
|
Передача параметров
|
Поддержка стека
|
Декорирование имен
|
Замечания
|
_cdecl |
Справа налево |
Аргументы из стека удаляет вызывающая программа. Это единственное соглашение о вызовах, которое допускает переменные списки аргументов |
Символ подчеркивания в качестве префикса перед именами функций, как в_Роо |
Используется по умолчанию для функций С и C++ |
_ stdcall |
Справа налево |
Свои собственные аргументы из стека удаляет сама вызванная функция |
Символ подчеркивания в качестве префикса перед именами функций и суффикс @ , за которым следует десятичное число байт в списке аргументов, как в_Роо@12 |
Используется почти всеми системными функциями и, по умолчанию, внутренними функциями Visual Basic |
_ fastcall |
Два первых DWORD-параметра передаются в регистрах ЕСХ и EDX; остальные передаются справа налево |
Аргументы из стека удаляет вызывающая функция |
Префикс @ перед именем и суффикс @ после него, за которым следует десятичное число байт в списке аргументов, как в ®Foo@12 |
Применяется только в Intel CPU. Это соглашение о вызовах используется по умолчанию для компиляторов Borland Delphi |
this |
Справа налево. Параметр this передается в регистре ЕСХ |
Аргументы из стека удаляет вызывающая функция |
Нет |
Используется автомат-ски методами классов C++, если не указан стандартный вызов. Все СОМ-методы объявляются со стандартным вызовом |
naked |
Справа налево |
Аргументы из стека удаляет вызывающая функция |
Нет |
Используются драйверами виртуальных устройств VxD и когда программист нуждается в собственном прологе и эпилоге |