Вопрос или проблема
Windows 10, Visual Studio 2022 Community. Модель x64. Вот файл сборки
.code
PUBLIC StubCode
StubCode PROC
mov rax, 4711
ret
StubCode ENDP
end
Вот часть на C++:
extern __int64 __cdecl StubCode();
int main()
{
__int64 n = StubCode();
return 0;
}
Файл сборки компилируется без ошибок. Параметры сборки->Настройка сборки установлена на masm.
Файл C++ показывает следующее:
error LNK2019: неразрешенный внешний символ “__int64 __cdecl StubCode(void)”
Подсказка по символам, которые определены и могут соответствовать: StubCode
Я также пробовал __stdcall и __fastcall. Никакой разницы.
Что не хватает компоновщику?
Решение:
extern “C” extern __int64 StubCode(void);
Вызов происходит через __fastcall независимо от сигнатуры.
Это означает, что первые четыре аргумента находятся в регистрах, остальные на стеке. Если вы хотите вызвать с аргументами:
.code
PUBLIC StubCode
;///////////////////////////////////////////////////////////////
;// Fastcall, rcx первый, rdx второй, r8 третий, r9 четвертый
;//
StubCode PROC
mov rax, [rsp + 40] ; Пятый
mov rax, [rsp + 48] ; Шестой
mov rax, [rsp + 56] ; Седьмой
mov rax, [rsp + 64] ; ... и так далее ...
ret
StubCode ENDP
end
Вызов с
__int64 n = StubCode(1, 2, 3, 4, 5, 6, 7, 8);
Ответ или решение
Ответ на вопрос: Ссылка на символ сборки не найдена
Описание проблемы:
Вы столкнулись с ошибкой компоновщика, которая сообщает о том, что не удается разрешить внешний символ __int64 __cdecl StubCode(void)
. Вы используете Windows 10 и Visual Studio 2022 Community для работы с проектом, который включает как ассемблерный, так и C++ код.
Код на ассемблере:
.code
PUBLIC StubCode
StubCode PROC
mov rax, 4711
ret
StubCode ENDP
end
Код на C++:
extern __int64 __cdecl StubCode();
int main()
{
__int64 n = StubCode();
return 0;
}
Причина ошибки:
Ошибка компоновщика LNK2019 указывает на то, что компоновщик не может найти реализацию символа StubCode
с той сигнатурой, которая указана в C++. Основная причина кроется в соглашении о вызовах и манере, как символы обрабатываются между C++ и ассемблером.
Когда вам нужно использовать ассемблерный код в C++, особенно если вы используете различные соглашения о вызовах (например, __cdecl
, __stdcall
, __fastcall
), необходимо убедиться, что символы экспортируются и объявляются правильно.
Решение:
-
Используйте
extern "C"
:
Чтобы предотвратить изменение имени символа при компиляции, оберните декларацию вашей функции вextern "C"
в C++ коде. Это остановит подавление именования и гарантирует, что символ будет доступен компоновщику.Измените вашу декларацию следующим образом:
extern "C" __int64 __cdecl StubCode();
-
Проверьте соглашение о вызовах:
Ваша функция может быть вызвана с использованием определенного соглашения. Убедитесь, что объявление функции в C++ соответствует ее реализации. Если вы хотите использовать__fastcall
, вам нужно будет изменить как декларацию, так и определение:На стороне C++:
extern "C" __int64 __fastcall StubCode();
На стороне ассемблера:
.code PUBLIC StubCode ;/////////////////////////////////////////////////////////////// ; Fastcall, rcx first, rdx second, r8 third, r9 fourth ; StubCode PROC mov rax, [rsp + 40] ; Пятый аргумент mov rax, [rsp + 48] ; Шестой аргумент mov rax, [rsp + 56] ; Седьмой аргумент mov rax, [rsp + 64] ; ... и так далее ... ret StubCode ENDP end
- Используйте правильный вызов функции:
Если вы измените соглашение на__fastcall
, вам нужно будет передать параметры, как показано ниже:__int64 n = StubCode(1, 2, 3, 4, 5, 6, 7, 8);
Заключение
Использование правильных соглашений о вызовах и грамотное формулирование деклараций функций между C++ и ассемблером решит вашу проблему с линковкой. Убедитесь, что оба компонента правильно синхронизированы, и ваша программа скомпилируется и свяжется без ошибок.