У меня есть сборка программы с msvc, которая динамически загружает dll. DLL предоставляет функцию, которая является вызовом из основной программы. Если оба они строятся с помощью msvc или gcc, все в порядке, но когда я компилирую, например. main с msvc и dll с gcc что-то не так.Вызов соглашения между msvc и gcc
# ifdef __GNUC__
# define CDECL __attribute__ ((__cdecl__))
# else
# define CDECL __cdecl
# endif
struct EXP result {
uint32_t code;
};
#define SUCCESS result{0};
virtual result CDECL foo(char const* const*& target) const {
target = (char const* const*)0xAFFE;
return SUCCESS;
}
Проблема заключается в том, после того, как целевой вызов является нулевым вместо 0xAFFE. Основная программа скомпилирована с использованием cdecl в качестве соглашения о вызове. Структура упакована (без выравнивания), но я также попытался выровняться по разным размерам (1, 2, 4, 8, 16). Я также попытался использовать __declspec/__atribute__(dllexport)
и различные комбинации обоих вариантов.
Если я взглянуть в код ассемблера, я могу видеть две большие разницы:
; msvc | gcc
;===============================|================================
; befor calling |
;-------------------------------|--------------------------------
| sub dword ptr [esp+4],8
|
; foo(); |
;-------------------------------|--------------------------------
push ebp | push ebp
mov ebp,esp | mov ebp,esp
mov eax,dword ptr [target] |
mov dword ptr [eax],0AFFEh |
mov eax,dword ptr [ebp+0Ch] | mov eax,dword ptr [ebp+0Ch]
mov dword ptr [eax],0 | mov dword ptr [eax],0AFFEh
| mov eax,0
pop ebp | pop ebp
ret | ret
Почему это, даже если я использую те же соглашения о вызовах как на компилятор? И как мне это исправить?
не является DLL вызовов должен быть STDCALL? –
@CemKalyoncu: Это не является обязательным требованием. Существует множество DLL-файлов, которые вместо этого используют '__cdecl'. Это абсолютно безопасно, если оба приложения и DLL согласны использовать '__cdecl'. Это не проблема. –
Функция возвращает структуру. Скорее всего, msvc и gcc не согласны с тем, как передать эту структуру вокруг, будь то в стеке вызовов или в регистрах и т. Д. Возврат структуры из функции не переносится. Вызывающая конвенция определяет, как передаются параметры, но не определяет, как передаются нетривиальные возвращаемые значения. Переносимое решение состоит в том, чтобы либо самостоятельно вернуть либо 'uint32_t', либо передать структуру в функцию в качестве параметра указателя, и позволить функции заполнять ее по мере необходимости. –