Stdcall c что это
Вызов функции с «неизвестным» именем на C++. Часть 1 — cdecl
The cdecl calling convention is used by many C systems for the x86 architecture. In cdecl, function parameters are pushed on the stack in a right-to-left order. Function return values are returned in the EAX register (except for floating point values, which are returned in the x87 register ST0). Registers EAX, ECX, and EDX are available for use in the function.
В общем, параметры передаются через стек в обратном порядке, результирующее значение будет в EAX кроме чисел с плавающей точкой — они будут в псевдо-стеке x87.
Составим план работы:
1) Сгенерировать некий буфер в памяти, который можно будет без изменений, пословно(4 байта) поместить в стек.
2) Узнать адрес функции, которую будем вызывать
3) Поместить в стек буфер по словам
4) Вызвать функцию
5) Выдернуть результат
Поехали!
Что же у нас есть:
1) char* sName — тут находится имя функции
2) int N — количество параметров
3) enum CParamType
4) CParamType Params[] — список типов параметров
5) void* ParamList[] — собственно, указатели на переменные с параметрами
6) CParamType RetType — тип данных результата
7) void* Ret — указатель на память, куда нужно скинуть результат
8) enum CCallConvention
9) CCallConvention conv — соглашение вызова. Для начала будем вызывать только cdecl функции
Это необходимый и достаточный список объявлений, которые нам нужны для вызова.
На C/C++ нету средств для осуществления этой операции, поэтому придется обратиться к ассемблеру.
1. Создаем буфер
Посчитали. Выделяем память:
void* Buffer = new char[4*WordCount];
Заполняем буфер: void*, int — помещаем без изменений, а в double меняем слова местами.
Думаю, тут комментировать нечего. offset — смещение по буферу.
2. Узнаем адрес функции
3. Помещаем в стек буфер по словам
Мы делаем цикл: пока ecx (WordCount) не станет 0, кладем в стек слово и уменьшаем ecx.
4. Вызываем функцию
Делаем
l2: call *%1;
после заполнения стека. %1 — указатель на функцию (addr).
5. Возвратить результат
Тут 2 варианта: целый результат или дробный. Согласно соглашению, по умолчанию результат будет в %eax, но если с плавающей точкой — то в всевдо-стеке x87.
1) Целый результат
movl %%eax, %0;
где %0 — переменная результата.
2) Вариант с плавающей точкой
По идее здесь нужно изъять из ST(0) ответ. Пока что у меня не получилось этого сделать. Хотелось бы увидеть в комментариях возможные решения. Заранее спасибо.
Ну вот и все! Задача была действительно не тривиальная. Надеюсь, этот пост кому-то понадобится.
PS Нужно все это для написания интерпретатора.
_________
Текст подготовлен в ХабраРедакторе
Stdcall c что это
Что значат __cdecl, __fastcall, и __stdcall специфики функций?
Объясните кто-нибудь понятным языком, что они делают, для чего были внедрены и где и зачем их применять?
А то чтото MSDN очень лаконичен.
| От: | Atilla |
Дата: | 24.12.02 07:58 | |
Оценка: |
Здравствуйте, LaFlour, Вы писали:
LF>Что значат __cdecl, __fastcall, и __stdcall специфики функций?
LF>Объясните кто-нибудь понятным языком, что они делают, для чего были внедрены и где и зачем их применять?
LF>А то чтото MSDN очень лаконичен.
| От: | Снорк |
Дата: | 24.12.02 07:59 | |
Оценка: |
Здравствуйте, LaFlour, Вы писали:
LF>Что значат __cdecl, __fastcall, и __stdcall специфики функций?
LF>Объясните кто-нибудь понятным языком, что они делают, для чего были внедрены и где и зачем их применять?
LF>А то чтото MSDN очень лаконичен.
Эти спецификаторы определяют порядок следования аргументов на стеке и сторону, которая стек очищает (caller или callee).
| От: | Bell |
Дата: | 24.12.02 08:04 | |
Оценка: |
Здравствуйте, LaFlour, Вы писали:
LF>Что значат __cdecl, __fastcall, и __stdcall специфики функций?
LF>Объясните кто-нибудь понятным языком, что они делают, для чего были внедрены и где и зачем их применять?
LF>А то чтото MSDN очень лаконичен.
LF>
Это есть так называемые calling convention, т.е. способы вызова функций (вернее способ передачи параметров):
как известно, перед вызовом функции ее параметры складываются в стэк
__cdecl — после вызова функции параметры из стэка удаляет вызывающая сторона
__stdcall — после вызова функции параметры из стэка удаляет сама эта функция
__fastcall — похожа на __stdcall, но часть параметров пытается передать через регистры.
| От: | Atilla |
Дата: | 24.12.02 08:06 | |
Оценка: |
Здравствуйте, Atilla, Вы писали:
Русские Блоги
Соглашения о вызовах __cdecl, __stdcall и __fastcall
Что такое соглашение о вызовах
функцияСоглашение о вызовахКак следует из названия, это ограничение и спецификация (спецификация) для вызовов функций, описывающая, как передаются параметры функций и кто очищает стек. Он определяет следующее: (1) порядок размещения параметров функции, (2) извлекает ли вызывающий или вызываемый объект параметры из стека и (3) метод, который генерирует измененное имя функции.
Историческая справка
До появления микрокомпьютеров производители компьютеров почти предоставили операционную систему и компиляторы, написанные для разных языков программирования. Соглашения о вызовах, используемые платформой, определяются программной реализацией поставщика. До появления Apple early ранние микрокомпьютеры были почти полностью «голыми», и была одна ОС или компилятор, даже IBM PC. Единственный стандарт оборудования для компьютеров, совместимых с IBM PC, был определен процессорами Intel (8086, 80386) и распространен IBM. Аппаратные расширения и все программные стандарты (соглашения о вызовах BIOS) открыты для конкуренции на рынке. Группа независимых компаний-разработчиков программного обеспечения предоставляет операционные системы, компиляторы для разных языков и некоторое прикладное программное обеспечение. Исходя из разных потребностей, исторических практик и творческого подхода разработчиков, все эти компании используют разные соглашения о вызовах, которые часто сильно различаются. После перетасовки рынка совместимых машин IBM доминировали операционные системы и инструменты программирования Microsoft (с различными соглашениями о вызовах). В это время компании второго уровня, такие как Borland и Novell, и проекты с открытым исходным кодом, такие как GCC, также сохранили свои собственные Ваши собственные стандарты. Правила совместимости в конечном итоге были приняты поставщиками оборудования и программными продуктами, что упростило задачу выбора допустимого стандарта.
Уборка номера
В этих соглашениях вызывающая сторона очищает аргументы в стеке, что позволяет реализовать списки переменных параметров, таких как printf ().
cdecl
cdecl (объявление C)Язык CСоглашение о вызовах также является стандартом де-факто для C. В архитектуре x86 его содержимое включает в себя:
Visual C++Определяет возвращаемое значение функции, если оноЗначение PODИ если длина не превышает 32 бита, она передается в регистр EAX, в диапазоне 33-64 бита она передается в регистр EAX: EDX, если длина превышает 64 бита или значение не POD, вызывающая сторона заранее выделяет пространство для возвращаемого значения функции, Адрес пространства передается как неявный параметр вызываемой функции.
GCCВозвращаемое значение функции выделяется вызывающей стороной, а адрес пространства передается в качестве неявного параметра вызываемой функции без использования регистра EAX. Начиная с версии 4.5 GCC, при вызове функций данные в стеке должны быть выровнены в 16B (в предыдущих версиях требовалось только 4B выравнивания).
Рассмотрим следующий фрагмент кода C:
На x86 создается следующий код сборки (синтаксис AT & T):
После возврата из функции вызываемая функция очищает стек. Есть некоторые различия в понимании cdecl, особенно в том, как возвращать значения. В результате программы x86 становятся несовместимыми после компиляции разными компиляторами на разных платформах ОС, даже если они используют правило «cdecl» и не используют системные вызовы. Некоторые компиляторы возвращают простые структуры данных, которые занимают приблизительно два регистра и помещаются в пару регистров EAX: EDX; более крупные структуры и объекты класса требуют некоторой специальной обработки обработчиком исключений (например, определенный конструктор, анализ Конструктор или присваивание), хранящиеся в памяти. Чтобы поместить его в память, вызывающей стороне необходимо выделить некоторую память и позволить указателю указать на эту память, этот указатель используется в качестве первого скрытого параметра, вызываемый использует эту память и возвращает всплывающие указатели при возврате. Скрытый указатель В Linux / GCC значения с плавающей точкой помещаются в стек через псевдостек x87. Как это:
Использование этого метода гарантирует, что стек может быть передан в правильном формате. Соглашение о вызовах cdecl обычно используется в качестве правила вызова по умолчанию для компилятора x86 C. Многие компиляторы также предоставляют возможность автоматического переключения соглашения о вызовах. Если вам нужно вручную указать правило вызова как cdecl, компилятор может поддерживать следующий синтаксис:
Модификатор _cdecl должен быть указан в прототипе функции, а другие параметры будут перезаписаны в объявлении функции.
syscall
Подобно cdecl, параметры помещаются в стек справа налево. EAX, ECX и EDX не сохраняют значения. Размер списка параметров помещается в регистр AL (?). syscall является стандартом для 32-битного OS / 2 API.
optlink
Уборка улиц
Если вызываемый объект хочет очистить параметры в стеке, он должен знать, сколько байтов в стеке нужно обработать на этапе компиляции. Поэтому этот тип соглашения о вызовах не совместим со списками переменных параметров, такими как printf (). Однако это соглашение о вызовах может быть более эффективным, потому что код, который необходимо разложить, не должен генерироваться при каждом вызове. Функции, использующие это правило, легко распознаются в коде asm, потому что перед возвращением они разбивают стек. Инструкция x86 ret позволяет опциональному 16-битному параметру указать количество байтов стека, используемых для разборки стека перед возвратом его вызывающей стороне. Код выглядит так:
pascal
Основываясь на соглашении о вызовах языка Pascal, параметры помещаются в стек слева направо (в отличие от cdecl). Вызываемый отвечает за очистку стека перед возвратом. Это соглашение о вызовах обычно встречается в компиляторах для следующих 16-битных платформ: OS / 2 1.x, Microsoft Windows 3.x и Borland Delphi версии 1.x.
register
Псевдоним для быстрого вызова Borland.
stdcall
Инструменты компиляции Microsoft предусматривают: PASCAL, WINAPI, APIENTRY, FORTRAN, CALLBACK, STDCALL, __far __pascal, __fortran, __stdcall Все относятся к таким соглашениям о вызовах.
fastcall
Это соглашение не было стандартизировано, и реализации не согласованы между компиляторами.
Соглашение __fastcall от Microsoft или GCC (то есть __msfastcall) передает первый (слева направо) параметр, который не превышает 32 бита, через регистр ECX / CX / CL, а второй параметр, который не превышает 32 бита, через регистр EDX. / DX / DL, остальные параметры помещаются в стек в порядке справа налево.
Слева направо передайте три параметра в EAX, EDX и ECX. Остальные параметры помещаются в стек слева направо. В 32-разрядном компиляторе Embarcadero Delphi это соглашение о вызовах по умолчанию, которое в компиляторе называется регистром. Некоторые версии на i386LinuxЭто соглашение также используется.
Вызов вызывающего абонента
thiscall
призваниеC++Используйте это соглашение для нестатических функций-членов. Существует две основные версии этого вызова, основанные на том, являются ли используемые компилятор и функция varargs. Для компилятора GCC этот вызов почти эквивалентен cdecl: вызывающая сторона очищает стек, а параметры передаются справа налево. Разница заключается в указателе this, thiscall помещает указатель this в стек в конце, что эквивалентно неявному параметру first-left в прототипе функции.
вMicrosoftVisual C++составительВ этом указатель this передается через регистр ECX, а остальное такое же, как в соглашении cdecl. Когда функция принимает переменный аргумент, вызывающая сторона отвечает за очистку стека (см. Cdecl). Это соглашение о вызовах явно указано только в Microsoft Visual C ++ 2005 и более поздних версиях. В других компиляторах thiscall не является ключевым словом (дизассемблеры, такие как IDA, используют __thiscall).
Intel ABI
Согласно Intel ABI, EAX, EDX и ECX могут свободно использоваться в процедурах или функциях и не требуют сохранения.
Соглашение о вызовах x86-64
Соглашение о вызовах x86-64 имеет больше регистров, которые можно использовать для передачи параметров. Более того, меньше несовместимых соглашений о вызовах, но есть еще два основных правила.
Соглашение о вызовах Microsoft x64
Например, функция имеет 5 целочисленных параметров, с первого по четвертый помещаются в регистры, а пятый помещается в верхнюю часть стека за пределами теневого пространства. Когда функция вызывается, этот стек используется для составления возвращаемого значения теневого пространства 32 бита + пятый параметр.
В архитектуре x86-64 Visual Studio 2008 хранит числа с плавающей запятой в XMM6 и XMM7 (также от XMM8 до XMM15). В результате для подпрограмм на языке ассемблера, написанных пользователем, XMM6 и XMM7 должны быть сохранены (эти два регистра не требуются для x86), что означает, что при переносе подпрограмм на ассемблере между x86 и x86-64 необходимо обратить внимание на / После этого XMM6 и XMM7 должны быть сохранены / восстановлены.
System V AMD64 ABI
Это соглашение в основном используется в Solaris, GNU / Linux, FreeBSD и других ОС, не относящихся к Microsoft. Первые шесть целочисленных параметров помещаются в регистры RDI, RSI, RDX, RCX, R8 и R9, в то же время от XMM0 до XMM7 используются для размещения аргументов с плавающей запятой. Для системных вызовов вместо RCX используется R10. Как и в соглашении Microsoft x64, другие дополнительные параметры помещаются в стек, а возвращаемое значение сохраняется в RAX. В отличие от Microsoft, нет необходимости предоставлять теневое пространство. При входе в функцию возвращаемое значение находится рядом с седьмым целочисленным параметром в стеке.
Мы знаем, что функция состоит из следующих частей: имя функции типа возвращаемого значения (список параметров), например:
【code1】
Вышеприведенный компонент является хорошо известным компонентом, на самом деле есть еще одна часть состава функции, то есть соглашение о вызовах. Следующим образом:
【code2】
Декларация и определение соглашения о вызовах должны быть одинаковыми
В VC ++ соглашение о вызовах является частью типа функции,Следовательно, соглашение о вызовах при объявлении и определении функции должно быть одинаковым. Соглашение о вызовах не может быть только в объявлении, и в определении нет или другое определение.Следующим образом:
[code3] Неправильно используйте один:
Сообщение об ошибке:
error C2373: ‘add’: redefinition; different type modifiers
error C2440: ‘initializing’: cannot convert from ‘int (__stdcall *)(int,int)’ to ‘int’
Выше хорошо, потому что по умолчанию __cdecl.
[Code4] Неправильное использование 2:
Сообщение об ошибке:
error C2373: ‘add’: redefinition; different type modifiers
error C2440: ‘initializing’: cannot convert from ‘int (__cdecl *)(int,int)’ to ‘int’
[Code5] Неправильное использование три:
Сообщение об ошибке:
error C2373: ‘add’: redefinition; different type modifiers
error C2440: ‘initializing’: cannot convert from ‘int (__stdcall *)(int,int)’ to ‘int’
[Code6] Правильное использование:
Вызов функции
Процесс вызова функции может быть описан следующим образом:
(1) Сначала поместите базовый адрес (ebp) стека вызывающего абонента (A) в стек, чтобы сохранить информацию предыдущей задачи.
(2) Затем присвойте значение верхнего указателя (esp) вызывающей стороны (A) для ebp в качестве нового базового адреса (то есть нижней части стека вызываемой стороны B).
(3) Затем откройте (обычно подинструкции) соответствующее пространство на этом базовом адресе (в нижней части стека вызываемого B) как пространство стека вызываемого B.
(4) После возврата из функции B ebp из текущего кадра стека восстанавливается до верхней части вызывающей стороны A (esp), так что вершина стека восстанавливает позицию до вызова функции B, затем Вызывающий объект A может вытолкнуть предыдущее значение ebp с вершины восстановленного стека (это можно сделать, потому что это значение помещается в стек перед вызовом функции). Таким образом, ebp и esp оба восстанавливают позицию перед вызовом функции B, то есть стек восстанавливает состояние до вызова функции B.
Этот процесс выполняется в сборке AT & T с помощью двух инструкций, а именно:
__cdecl Особенности
(1). Чтобы убедиться, что параметры помещены в стек справа налево, мы можем взглянуть на следующий код: Отладка для одношаговой отладки, вы можете видеть, что наш стек вызовов сначала вводит GetC (), затем GetB ( ) И, наконец, введите GetA ().
С точки зрения отладки кода и программ нам не нужно обращать внимание на порядок добавления стека и очистки стека, потому что это определяется компилятором, и мы не можем его изменить. Но третий момент часто беспокоит нас, потому что, если мы этого не понимаем, часто возникают необъяснимые ошибки, когда несколько библиотек (таких как dll, lib, exe) вызывают и зависят друг от друга. Я подробно расскажу об этом в следующих главах.
__stdcall Особенности
__fastcall Особенности
__thiscall
резюме
Вот краткое изложение различий между _cdecl, _stdcall и __fastcall:
Что такое stdcall?
Я изучаю Программирование Win32 и WinMain прототип выглядит так:
Я был смущен тем, что это WINAPI идентификатор был и нашел:
что это значит? Я смущен тем, что у этого есть что-то вообще после типа возврата. Что такое __stdcall для чего? Что это значит, когда есть что-то между типом возврата и именем функции?
8 ответов
это в первую очередь имеет значение, когда вы вызываем функцию вне вашего кода (например, API ОС) или ОС вызывает вас (как в случае с WinMain). Если компилятор не знает правильного соглашения о вызове, вы, вероятно, получите очень странные сбои, поскольку стек не будет управляться правильно.
C или c++ сами по себе не определяют эти идентификаторы. Они являются расширениями компилятора и соответствуют определенным соглашениям о вызовах. Это определяет, куда ставить аргументы, в каком порядке, где вызываемая функция найдет обратный адрес и так далее. Например, __fastcall означает, что аргументы функций передаются через регистры.
на Статья В Википедии предоставляет обзор различных соглашений о вызовах, найденных там.
ответы до сих пор охватывали детали, но если вы не собираетесь опускаться до сборки, то все, что вам нужно знать, это то, что и вызывающий, и вызываемый должны использовать одно и то же соглашение о вызове, иначе вы получите ошибки, которые трудно найти.
Я согласен, что все ответы, которые до сих пор верны, но вот почему. Компиляторы Microsoft C и c++ предоставляют различные соглашения о вызовах для (предполагаемой) скорости вызовов функций в функциях C и C++ приложения. В каждом случае вызывающий абонент и вызываемый абонент должны договориться о том, какое соглашение о вызове использовать. Теперь Windows сама предоставляет функции (API), и они уже были скомпилированы, поэтому, когда вы их вызываете, вы должны соответствовать им. Любые вызовы API Windows и обратные вызовы из API-интерфейсов Windows необходимо использовать соглашение __stdcall.
Чем отличается функция с __stdcall и без
И еще в макросе заметил что он тоже добавляет эту штуку, от в этом макросе IMPLEMENT_DYNCREATE(CSpaceship,CCmdTarget)
Создается функция которая возвращает указатель на объект CSpaceship, вроде ее определение создается вида:
Чем отличается if от (?:)
Здравствуйте. Почитываю С++, сам программирую в Делфи. Вот немного запутался. В делфи есть условный.
Чем new отличается от malloc?
Чем new отличается от malloc?
Оно ж для API Win32 используется.
Я так понял это не важно для разработки, это как бы конечный этап типо оптимизации, когда уже все будет готово, потом уже можно над этим позаморачиватся если нужно будет
Добавлено через 1 минуту
Можем.
Например, мы пользуемся WinAPI, например, есть некая API функция, которая принимает callback. И этот callback она ждет в соответствии с stdcall. Тогда функцию, которую мы делаем callback’ом, нужно обозначить как __stdcall, чтобы ее вызов внутри API функции был корректным.
-=ЮрА=-, только собрался открывать картинку, что бы посмотреть, а Вы тут со своим «DELDEL»:D
Ну и хорошие статьи/примеры всегда интересно почитать 🙂 и не только ТС
Например, мы пользуемся WinAPI, например, есть некая API функция, которая принимает callback. И этот callback она ждет в соответствии с stdcall. Тогда функцию, которую мы делаем callback’ом, нужно обозначить как __stdcall, чтобы ее вызов внутри API функции был корректным.