[Из песочницы] Assembler вместе с C++ в Visual Studio 2013
Идея этой статьи отнюдь не новая, но, поскольку мне пришлось потратить два дня на разбор всех ошибок компиляции и линковки, а также поиск ответов на свои вопросы, решил, что читатели Хабра заслуживают на экономию времени. Тех, кто желает быстро узнать, как использовать одновременно *.asm и *.cpp файлы в проекте, как вызывать методы C++ из ассемблера и наоборот, прошу пожаловать под кат.ПредисловиеНачалось все с прочтения мной публикации «Ассемблер для Windows используя Visual Studio» (отсюда и почти идентичный код). Там рассмотрено использование Visual Studio 2005, а для 2013-й студии процесс похожий, но есть несколько отличий, которые заставят неподготовленного пользователя долго искать решения всех проблем со сборкой.Содержание TL; DR Создание проекта Настройка подсветки синтаксиса Тонкости вызова методов между С++ и Asm Приложение TL; DR Для тех, у кого совсем нет времени на прочтение: в конце статьи (в приложении) есть ссылка на готовый шаблон проекта и на аддон для подсветки синтаксиса.Создание проекта Иллюстрированная версия Включаем Visual Studio, выбираем File → New → Project…:
Выбираем шаблон Win32 Console Application, кликаем ОК:
Жмем Next:
Ставим галочку напротив Empty project и жмем Finish:
Создаем исходники. Для этого делаем правый клик на Source Files, выбираем Add → New Item…:
Выбираем C++ File и жмем Add:
Аналогично, создаем *.asm файл (просто меняем расширение в поле Name):
Важно: имена файлов должны быть разными (не учитывая расширение), иначе при создании файлов *.obj возникнет проблема перезаписи одного обьектного файла другим.
Теперь настройки. Делаем правый клик на проекте, выбираем Build Dependencies → Build Customizations…
Ставим галочку напротив masm и жмем ОК:
Делаем правый клик на файле *.asm, выбираем Properties…:
В поле Item Type выбираем Microsoft Macro Assembler и жмем ОК:
Выбираем Project → Properties…:
Выбираем Configuration Properties → Microsoft Macro Assembler → Listing File. В поле Assembled Code Listing File вводим $(ProjectName).lst:
Выбираем Configuration Properties → Linker → Advanced. В поле Image Has Safe Exception Handlers выбираем значение No. Жмем ОК:
На этом этапе проект можно считать созданным. Написание кода рассмотрено в секции Тонкости вызова методов между С++ и Asm.
Только текст Включаем Visual Studio, выбираем File → New → Project…Выбираем шаблон Win32 Console Application, кликаем ОК.
Жмем Next.
Ставим галочку напротив Empty project и жмем Finish.
Создаем исходники. Для этого делаем правый клик на Source Files, выбираем Add → New Item…
Выбираем C++ File и жмем Add.
Аналогично, создаем *.asm файл (просто меняем расширение в поле Name).
Важно: имена файлов должны быть разными (не учитывая расширение), иначе при создании файлов *.obj возникнет проблема перезаписи одного объектного файла другим.
Теперь настройки. Делаем правый клик на проекте, выбираем Build Dependencies → Build Customizations…
Ставим галочку напротив masm и жмем ОК.
Делаем правый клик на файле *.asm, выбираем Properties…
В поле Item Type выбираем Microsoft Macro Assembler и жмем ОК.
Выбираем Project → Properties…
Выбираем Configuration Properties → Microsoft Macro Assembler → Listing File. В поле Assembled Code Listing File вводим $(ProjectName).lst.
Выбираем Configuration Properties → Linker → Advanced. В поле Image Has Safe Exception Handlers выбираем значение No. Жмем ОК.
На этом этапе проект можно считать созданным. Написание кода рассмотрено в секции Тонкости вызова методов между С++ и Asm.
Настройка подсветки синтаксиса Существует аддон для Visual Studio — asmHighlighter, однако на момент написания статьи версии для VS2013 не существовало. Однако, просмотрев раздел Discussions, я нашел сообщение пользователя Trass3r, который, к счастью, поделился репозиторием с версией аддона для VS2013. После установки Visual Studio SDK мне удалось собрать проект и теперь *.vsix пакет есть в свободном доступе.Тонкости вызова методов между С++ и Asm Для того, чтоб избежать ошибок компиляции и/или связывания нужно помнить следующее: Если надо вызывать из ассемблера библиотечные методы, достаточно в начале секции кода указать, какие именно методы мы собираемся использовать. EXTRN printf: proc; we’ll use printf Далее можно просто использовать call: ; printf (ebx, eax) push eax; push ebx call printf add esp, 8; pop x2 Если же надо вызывать пользовательские методы, то кроме п.1 надо еще писать extern «C» перед определением метода. extern «C» void* readName () { char* name = (char*)calloc (1, 255); scanf (»%s», name); while (getchar () != '\n'); return name; } Соответственно, в *.asm файле: EXTRN readName: proc; and void* readName () и call readName; eax = readName () В случае использования Asm-методов в C++ достаточно лишь указать прототип: extern «C» { void sayHello (); } Данный прототип соответствует такому объявлению Asm-метода: sayHello PROC
call readName; eax = readName () lea ebx, helloFormat; ebx = &helloFormat
; printf (ebx, eax) push eax push ebx call printf add esp, 8; pop x2
retn
sayHello ENDP
Собственно, полный исходный код примера: Source.cpp
#define _CRT_SECURE_NO_WARNINGS
#include
extern «C» { void sayHello (); }
void main () { printf («Hello, what is your name?\n»); sayHello (); while (getchar () != '\n'); } extern «C» void* readName () { char* name = (char*)calloc (1, 255); scanf (»%s», name); while (getchar () != '\n'); return name; } AsmSource.asm .686 .MODEL FLAT, C .STACK .DATA ;-----------Local data------------------------------ helloFormat BYTE «Hello, %s!», 10, 13, 0 .CODE ;-----------External usage-------------------------- EXTRN printf: proc;// we’ll use printf EXTRN readName: proc;//and void* readName () ;-----------Function definitions-------------------- sayHello PROC
call readName; eax = readName () lea ebx, helloFormat; ebx = &helloFormat
; printf (ebx, eax) push eax push ebx call printf add esp, 8; pop x2
retn
sayHello ENDP
END Приложение Готовый шаблон проекта можно найти здесь.Пакет для подсветки asm синтаксиса можно найти здесь.