Идея этой статьи отнюдь не новая, но, поскольку мне пришлось потратить два дня на разбор всех ошибок компиляции и линковки, а также поиск ответов на свои вопросы, решил, что читатели Хабра заслуживают экономии времени. Тех, кто желает быстро узнать, как использовать одновременно *.asm и *.cpp файлы в проекте, как вызывать методы C++ из ассемблера и наоборот, прошу пожаловать под кат.
Началось все с прочтения мной публикации «Ассемблер для Windows используя Visual Studio» (отсюда и почти идентичный код). Там рассмотрено использование Visual Studio 2005, а для 2013-й студии процесс похожий, но есть несколько отличий, которые заставят неподготовленного пользователя долго искать решения всех проблем со сборкой.
Для тех, у кого совсем нет времени на прочтение: в конце статьи (в приложении) есть ссылка на готовый шаблон проекта и на аддон для подсветки синтаксиса.
Существует аддон для Visual Studio — asmHighlighter, однако на момент написания статьи версии для VS2013 не существовало. Однако, просмотрев раздел Discussions, я нашел сообщение пользователя Trass3r, который, к счастью, поделился репозиторием с версией аддона для VS2013. После установки Visual Studio SDK мне удалось собрать проект и теперь *.vsix пакет есть в свободном доступе.
Для того, чтоб избежать ошибок компиляции и/или связывания нужно помнить следующее:
Собственно, полный исходный код примера:
Готовый шаблон проекта можно найти здесь.
Пакет для подсветки asm синтаксиса можно найти здесь.
P.S. спасибо ilynxy за исправление «заслуживают на»)
Предисловие
Началось все с прочтения мной публикации «Ассемблер для Windows используя Visual Studio» (отсюда и почти идентичный код). Там рассмотрено использование Visual Studio 2005, а для 2013-й студии процесс похожий, но есть несколько отличий, которые заставят неподготовленного пользователя долго искать решения всех проблем со сборкой.
Содержание
- TL;DR
- Создание проекта
- Настройка подсветки синтаксиса
- Тонкости вызова методов между С++ и Asm
- Приложение
TL;DR
Для тех, у кого совсем нет времени на прочтение: в конце статьи (в приложении) есть ссылка на готовый шаблон проекта и на аддон для подсветки синтаксиса.
Создание проекта
Иллюстрированная версия
Включаем Visual Studio, выбираем File -> New -> Project...:
![image](https://habrastorage.org/r/w1560/files/8d2/c85/346/8d2c8534671449e79621fb9e2fcfb389.png)
Выбираем шаблон Win32 Console Application, кликаем ОК:
![image](https://habrastorage.org/r/w1560/files/c36/0ec/cd2/c360eccd2e9a4d08872d5011cd55bc4d.png)
Жмем Next:
![image](https://habrastorage.org/r/w1560/files/ec9/76c/968/ec976c96817e439b825b82d3b7cba72a.png)
Ставим галочку напротив Empty project и жмем Finish:
![image](https://habrastorage.org/r/w1560/files/564/aea/db2/564aeadb209342459df8a625e5948d4f.png)
Создаем исходники. Для этого делаем правый клик на Source Files, выбираем Add -> New Item...:
![image](https://habrastorage.org/r/w1560/files/613/fc6/417/613fc64174924fbe9fd402fc4abe7575.png)
Выбираем C++ File и жмем Add:
![image](https://habrastorage.org/r/w1560/files/1bf/89c/339/1bf89c33992840378f5031df3a4dff91.png)
Аналогично, создаем *.asm файл (просто меняем расширение в поле Name):
![image](https://habrastorage.org/r/w1560/files/848/40f/173/84840f17347649ab85575914eb733a02.png)
Важно: имена файлов должны быть разными (не учитывая расширение), иначе при создании файлов *.obj возникнет проблема перезаписи одного обьектного файла другим.
Теперь настройки. Делаем правый клик на проекте, выбираем Build Dependencies -> Build Customizations...
![image](https://habrastorage.org/r/w1560/files/1de/a34/25a/1dea3425a605412cab718678f8f52476.png)
Ставим галочку напротив masm и жмем ОК:
![image](https://habrastorage.org/r/w1560/files/0c3/f1d/206/0c3f1d206a6549608ea1d29ac84ee630.png)
Делаем правый клик на файле *.asm, выбираем Properties...:
В поле Item Type выбираем Microsoft Macro Assembler и жмем ОК:
![image](https://habrastorage.org/r/w1560/files/3a6/f1d/4df/3a6f1d4dfa7f4bf0b14b246920cbb772.png)
Выбираем Project -> Properties...:
![image](https://habrastorage.org/r/w1560/files/8cf/122/ca4/8cf122ca471246829aa40c9c2e5791eb.png)
Выбираем Configuration Properties -> Microsoft Macro Assembler -> Listing File. В поле Assembled Code Listing File вводим $(ProjectName).lst:
![image](https://habrastorage.org/r/w1560/files/b69/a1b/890/b69a1b8902824eb39eba1f5dafcf108f.png)
Выбираем Configuration Properties -> Linker -> Advanced. В поле Image Has Safe Exception Handlers выбираем значение No. Жмем ОК:
![image](https://habrastorage.org/r/w1560/files/074/22f/538/07422f538fa940d885331fe56cedc9bb.png)
На этом этапе проект можно считать созданным. Написание кода рассмотрено в секции Тонкости вызова методов между С++ и Asm.
![image](https://habrastorage.org/files/8d2/c85/346/8d2c8534671449e79621fb9e2fcfb389.png)
Выбираем шаблон Win32 Console Application, кликаем ОК:
![image](https://habrastorage.org/files/c36/0ec/cd2/c360eccd2e9a4d08872d5011cd55bc4d.png)
Жмем Next:
![image](https://habrastorage.org/files/ec9/76c/968/ec976c96817e439b825b82d3b7cba72a.png)
Ставим галочку напротив Empty project и жмем Finish:
![image](https://habrastorage.org/files/564/aea/db2/564aeadb209342459df8a625e5948d4f.png)
Создаем исходники. Для этого делаем правый клик на Source Files, выбираем Add -> New Item...:
![image](https://habrastorage.org/files/613/fc6/417/613fc64174924fbe9fd402fc4abe7575.png)
Выбираем C++ File и жмем Add:
![image](https://habrastorage.org/files/1bf/89c/339/1bf89c33992840378f5031df3a4dff91.png)
Аналогично, создаем *.asm файл (просто меняем расширение в поле Name):
![image](https://habrastorage.org/files/848/40f/173/84840f17347649ab85575914eb733a02.png)
Важно: имена файлов должны быть разными (не учитывая расширение), иначе при создании файлов *.obj возникнет проблема перезаписи одного обьектного файла другим.
Теперь настройки. Делаем правый клик на проекте, выбираем Build Dependencies -> Build Customizations...
![image](https://habrastorage.org/files/1de/a34/25a/1dea3425a605412cab718678f8f52476.png)
Ставим галочку напротив masm и жмем ОК:
![image](https://habrastorage.org/files/0c3/f1d/206/0c3f1d206a6549608ea1d29ac84ee630.png)
Делаем правый клик на файле *.asm, выбираем Properties...:
![image](https://habrastorage.org/files/589/728/295/58972829532b4d4a837c766c4d29950f.png)
В поле Item Type выбираем Microsoft Macro Assembler и жмем ОК:
![image](https://habrastorage.org/files/3a6/f1d/4df/3a6f1d4dfa7f4bf0b14b246920cbb772.png)
Выбираем Project -> Properties...:
![image](https://habrastorage.org/files/8cf/122/ca4/8cf122ca471246829aa40c9c2e5791eb.png)
Выбираем Configuration Properties -> Microsoft Macro Assembler -> Listing File. В поле Assembled Code Listing File вводим $(ProjectName).lst:
![image](https://habrastorage.org/files/b69/a1b/890/b69a1b8902824eb39eba1f5dafcf108f.png)
Выбираем Configuration Properties -> Linker -> Advanced. В поле Image Has Safe Exception Handlers выбираем значение No. Жмем ОК:
![image](https://habrastorage.org/files/074/22f/538/07422f538fa940d885331fe56cedc9bb.png)
На этом этапе проект можно считать созданным. Написание кода рассмотрено в секции Тонкости вызова методов между С++ и 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.
Выбираем шаблон 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 <stdio.h>
#include <stdlib.h>
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 синтаксиса можно найти здесь.
P.S. спасибо ilynxy за исправление «заслуживают на»)