В этой статье мы рассмотрим процессы компиляции и компоновки программ на языке ассемблер для для операционных систем DOS и Windows. Напомню что компиляция — это процесс перевода файлов программы в так называемые объектные файлы. Объектные файлы — это файлы, в которых находится почти готовая программа в виде разрозненных кусков кода, связанных некоторой служебной информацией. Чтобы превратить эти куски кода в готовую программу используется компоновщик. Он выстраивает код в исполняемом файле в нужном порядке и создает готовый файл заданного формата. Этот получившийся файл уже можно исполнять.
Начнем с компиляции под DOS
В DOS существовало два вида исполняемых файлов: *.com и *.exe. Файлы *.com характеризуются тем, что весь код программы, данные и место для стека содержится в одном сегменте памяти, причем код программы должен находиться в сегменте со смещением в 256 байт (100h). Файлы формата *.exe не имеют ограничений на количество используемых сегментов памяти.
Для компиляции под DOS я использую компилятор MASM версии 6.11 и TASM версии 5.3
Компиляция и компоновка файлов *.com с помощью TASM:
\tasm32\bin\tasm53.exe test.asm, test.obj, test.lst /z /t /ml /m2 /l \tasm32\bin\tlink.exe test.obj /x /t /3
Компиляция и компоновка файлов *.exe с помощью TASM:
\tasm32\bin\tasm53.exe test.asm, test.obj, test.lst /z /t /ml /m2 /l \tasm32\bin\tlink test.obj, test.exe /x /3
Описание всех параметров можно получить, выполнив в командной строке tasm53.exe /? и tlink.exe /?
Компилятор MASM позволяет выполнять компиляцию и компоновку через вызов своей программы-оболочки ml.exe
Компиляция файлов *.com с помощью MASM:
\masm611\bin\ml.exe test.asm /AT /Fl /link
Компиляция файлов *.exe с помощью MASM:
\masm611\bin\ml.exe test.asm /Fl /link
Компиляция под Windows
Для компиляции под Windows используются компилятор MASM32 и TASM32.
Компиляция и компоновка файлов *.exe с помощью TASM32:
\tasm32\bin\tasm32.exe test.asm, test.obj, test.lst /zi /t /ml /m3 /q \tasm32\bin\tlink32 /x /Tpe /ap /c /V4.0 test.obj, test.exe, , user32.lib
Компиляция файлов *.exe с помощью MASM32:
\masm32\bin\ml.exe /c /coff test.asm \masm32\bin\link.exe /SUBSYSTEM:WINDOWS test.obj
После компиляции
После компиляции и компоновки у вас должен получиться исполняемый файл *.exe или *.com формата, в зависимости от того, какие параметры вы задавали компилятору.
Последнее обновление: 01.07.2023
Установка MASM
Для работы с MASM надо установить для Visual Studio инструменты разработки для C/C++. Поэтому после загрузки программы установщика Visual Studio запустим ее и в окне устанавливаемых
опций выберем пункт Разработка классических приложений на C++:
Visual Studio включает как 32-разрядные, так и 64-разрядные версии MASM. 32-раздяная версия представляет файл ml.exe,
а 64-разрядная — файл ml64.exe. Точное расположение файлов может варьироваться от версии Visual Studio. Например, в моем случае это папка
C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.35.32215\bin\Hostx64\x64
Для использования MASM64 перейдем к меню Пуск и в списке программ найдем пункт Visual Studio и подпункт
x64 Native Tools Command Prompt for VS 2022
Нам должна открыться консоль. Введем в нее ml64, и нам отобразится версия ассемблера и некоторая дополнительная информация:
********************************************************************** ** Visual Studio 2022 Developer Command Prompt v17.5.5 ** Copyright (c) 2022 Microsoft Corporation ********************************************************************** [vcvarsall.bat] Environment initialized for: 'x64' C:\Program Files\Microsoft Visual Studio\2022\Community>ml64 Microsoft (R) Macro Assembler (x64) Version 14.35.32217.1 Copyright (C) Microsoft Corporation. All rights reserved. usage: ML64 [ options ] filelist [ /link linkoptions] Run "ML64 /help" or "ML64 /?" for more info C:\Program Files\Microsoft Visual Studio\2022\Community>
Стоит отметить, что запуск этой этой утилиты фактически представляет запуск файла C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvars64.bat —
он по сути вызывает другой файл — vcvarsall.bat, который собственно и настраивает окружение для выполнения ассемблера.
Структура программы на ассемблере MASM
Типичная программа на MASM содержит одну или несколько секций, которые определяют, как содержимое программы будет располагаться в памяти. Эти секции
начинаются с таких директив MASM, как .code или .data. Данные, используемые в программе, обычно определяются в секции .data
.
Инструкции ассембра определяются в секции .code
.
В общем случае программа на ассемблере MASM имеет следующий вид:
.code main proc ret main endp end
Директива .code указывает MASM сгруппировать операторы, следующие за ней, в специальный раздел памяти, зарезервированный для машинных инструкций.
Ассемблер преобразует каждую машинную инструкцию в последовательность из одного или нескольких байт. CPU интерпретирует эти значения байт как машинные инструкции во
время выполнения программы.
Далее с помощью операторов main proc определяется процедура main. Операторы main endp указывают на конец функции main.
Между main proc
и main endp
располагаются выполняемые инструкции ассемблера. Причем в самом конце функции идет инструкция ret,
с помощью которой выполнение возвращается в окружение, в котором была вызвана даннуа процедура.
В конце файла кода идет инструкция end
Программа может содержать комментарии, которые располагаются после точки с запятой:
.code ; начало секции с кодом программы main proc ; Функция main ret ; возвращаемся в вызывающий код main endp ; окончание функции main end ; конец файла кода
Комментарии на работу программы никак не влияют и при компиляции не учитываются.
Компиляция программы
Компиляция программы на MASM обычно происходит в командной строке. Например, воспользуемся кодом выше и напишем простейшую программу на ассемблере, которая ничего
не делает. Для этого определим на жестком диске папку для файлов с исходным кодом. Допустим, она будет называться
C:\asm. И в этой папке создадим новый файл, который назовем hello.asm и в котором определим следующий код:
.code ; начало секции с кодом программы main PROC ; Функция main ret ; возвращаемся в вызывающий код main ENDP END ; конец файла кода
Откроем программу x64 Native Tools Command Prompt for VS 2022 и перейдем в ней к папке, где располагается файл hello.asm. Затем выполним следующую команду
ml64 hello.asm /link /entry:main
В данном случае вызываем приложение ml64.exe и передаем ему для компиляции файл hello.asm. А флаг /link
указывает MASM
скомпоновать скомпилированный файл в файл приложения exe, а все дальнейшие параметры (в частности, параметр /entry:main
) передаются компоновщику.
Параметр /entry:main
передает компоновщику имя основной процедуры/функции, с которой начинается выполнение программы.
Компоновщик сохраняет этот адрес этой процедуры/функции в специальном месте исполняемого файла, чтобы Windows могла определить начальный адрес основной программы после загрузки исполняемого файла в память.
В результате ассемблер скомпилирует ряд файлов
********************************************************************** ** Visual Studio 2022 Developer Command Prompt v17.5.5 ** Copyright (c) 2022 Microsoft Corporation ********************************************************************** [vcvarsall.bat] Environment initialized for: 'x64' C:\Program Files\Microsoft Visual Studio\2022\Community>cd c:\asm c:\asm>ml64 hello.asm /link /entry:main Microsoft (R) Macro Assembler (x64) Version 14.35.32217.1 Copyright (C) Microsoft Corporation. All rights reserved. Assembling: hello.asm Microsoft (R) Incremental Linker Version 14.35.32217.1 Copyright (C) Microsoft Corporation. All rights reserved. /OUT:hello.exe hello.obj /entry:main c:\asm>
В итоге в каталоге программы будут сгенерированы объектный файл hello.obj и собственно файл программы — hello.exe.
Время на прочтение
4 мин
Количество просмотров 142K
Многие из нас изучали ассемблер в университете, но почти всегда это ограничивалось простыми алгоритмами под DOS. При разработке программ для Windows может возникнуть необходимость написать часть кода на ассемблер, в этой статье я хочу рассказать вам, как использовать ассемблер в ваших программах под Visual Studio 2005.
Создание проекта
В статье мы рассмотрим как вызывать ассемблер из С++ кода и обратно, передавать данные, а также использовать отладчик встроенный в Visual Studio 2005 для отладки кода на ассемблер.
Для начала нам нужно создать проект. Включаем Visual Studio, выбираем File > New > Project. В Visual Studio нет языка ассемблер в окне выбора типа проекта, поэтому создаем С++ Win32 проект. В окне настроек нового проекта выбираем «Empty Project».
По умолчанию Visual Studio не распознает файлы с кодом на ассемблер. Для того чтобы включить поддержку ассемблер нам необходимо настроить в проекте условия сборки указав какой программой необходимо компилировать файлы *.asm. Для этого выбираем пункт меню «Custom Build Rules…».
В открывшемся окне мы можем указать специальные правила компиляции для различных файлов, Visual Studio 2005 уже имеет готовое правило для файлов *.asm, нам необходимо лишь включить его, установив напротив правила «Microsoft Macro Assembler» галочку.
Добавление исходного кода
Перейдем к написанию исходного кода нашего проекта. Начнем с добавления исходного кода на c++. Добавим новый файл в папку Source Files. В качестве Template выбираем C++ File и вводим желаемое имя файла, например main.cpp. Напишем функцию, которая будет считывать имя введенное пользователем, оформив это в виде функции readName() которая будет возвращать ссылку на считанное имя. Мы получим примерно следующее содержимое файла:
#include <stdio.h> void main () { printf("Hello, what is your name?\n"); } void* readName() { char name[255]; scanf("%s", &name); return &name; }
Теперь, когда мы знаем имя пользователя мы можем вывести приветствие, его будет выводить функция sayHello() которую мы напишем на ассемблер, чтобы использовать эту функцию сначала мы должны указать что она будет определена в другом файле, для этого добавим блок к main.cpp:
extern "C" { void sayHello(); }
Этот блок говорит компилятору, что функция sayHello() будет объявлена в другом файле и будет иметь правила вызова «C». Компилятор C++ искажает имена функций так, что указание правил вызова обязательно. Кроме того мы хотим использовать функцию readName() из функции sayHello(), для этого необходимо добавить extern «C» перед определением функции readName(), это позволит вызывать эту функцию из других файлов используя правила вызова «C».
Пришло время добавить код на ассемблер, для этого добавим в Source Folder новый файл. Выбираем тип Text File (.txt) и в поле название заменяем .txt на .asm, назовем наш файл hello.asm. Объявим функцию sayHello() и укажем внешние функции, которые мы хотим использовать. Получим следующий код:
.686 .MODEL FLAT, C .STACK .DATA helloFormat BYTE "Hello %s!",10,13,0 .CODE readName PROTO C printf PROTO arg1:Ptr Byte, printlist: VARARG sayHello PROC invoke readName invoke printf, ADDR helloFormat, eax ret sayHello ENDP END
Теперь мы можем запустить проект, для этого просто выбираем Debug > Start Without Debugging или нажимаем комбинацию Ctrl-F5. Если все сделано верно, вы увидите окно программы:
Немного усложним задачу, попробуем написать на ассемблер функцию принимающую параметр и возвращающую значение. Для примера напишем функцию calcSumm() которая будет принимать целое число и возвращать сумму его цифр. Изменим наш код на С++ добавив в него информацию о функции calcSumm, ввод числа и собственно вызов функции. Добавим функцию в файл hello.asm, возвращаемое значение помещается в eax, параметры объявляются после ключевого слова PROC. Все параметры можно использовать в коде процедуры, они автоматически извлекутся из стека. Также в процедурах можно использовать локальные переменные. Вы не можете использовать эти переменные вне процедуры. Они сохранены в стеке и удаляются при возврате из процедуры:
.686 .MODEL FLAT, C .STACK .DATA helloFormat BYTE "Hello %s!",10,13,0 .CODE readName PROTO C printf PROTO arg1:Ptr Byte, printlist: VARARG sayHello PROC invoke readName invoke printf, ADDR helloFormat, eax ret sayHello ENDP calcSumm PROC a:DWORD xor esi, esi mov eax, a mov bx, 10 @div: xor edx, edx div bx add esi, edx cmp ax, 0 jne @div mov eax, esi ret calcSumm ENDP END
Запустив проект мы увидим следующий результат выполнения:
Отладка
Конечно в данной задаче нет ничего сложного и она вовсе не требует использования ассемблер. Более интересным будет рассмотреть, а что же нам дает Visual Studio для разработки на ассемблер. Попробуем включить режим отладки и установим точку остановки в hello.asm, запустим проект, мы увидим следующее:
Окно Disassembly (Debug > Windows > Disassembly) показываем команды ассемблер для данного объектного файла. Код который мы написали на С++ показывается черным цветом. Disassembled code показывается серым после соответствующего ему кода на C++/ассемблер. Окно Disassembly позволяет отлаживать код и осуществлять stepping по нему.
Окно регистров (Debug > Windows > Registers) позволяет посмотреть значение регистров.
Окно памяти (Debug > Windows > Memory) позволяет посмотреть дамп памяти, слева мы видим шестнадцатеричные адрес, справа шеснадцатеричные значения соответствующих ячеек памяти, можно перемещаться, вводя адрес в соответствующее поле в верху окна.
In this guide you will learn how to compile a MASM assembly file into a Windows executable.
Prerequisites
Before we begin you’re going to need to install MASM32. I’ve written a guide on how to do that. MASM includes the required compiler ml.exe and the linker link.exe. The documentation for each are below.
- Compiler: https://docs.microsoft.com/en-us/cpp/assembler/masm/ml-and-ml64-command-line-reference?view=vs-2017
- Linker: https://docs.microsoft.com/en-us/cpp/build/reference/linker-options?view=vs-2017
Once you have MASM installed copy the sample code below and save it as program.asm:
.386
.model flat,stdcall
.stack 4096
.code
main PROC
nop
main ENDP
END main
Our executable will do absolutely nothing. It will be used to test compilation and linking. Let’s do that …
The Not So Easy Way
Some time in the past it was possible to run ml.exe and have it produce an executable. This method has never worked for me for reasons I will discuss in a moment:
ml.exe program.asm
On my system this produces the following error:
LINK : warning LNK4044: unrecognized option "z2"; ignored
This error occurs because the compiler attempts to compile and link, but if your ml.exe and link.exe versions do not match you will run into this same problem.
The reason is because ml.exe is attempting to pass the /z2 option to link.exe, but the linker doesn’t recognize the option. This is discussed in a bit more detail here.
Fear not my friends, there is a solution!
The Other Way
Instead of expecting ml.exe to call link.exe you can instead compile the assembly into .obj file and call link.exe yourself. An equally simple solution.
You do that like so:
ml.exe /c /coff program.asm
link.exe /subsystem:windows program.obj
The first line compiles the MASM assembly file into an COFF (common object file format) file. The /c option tells ml.exe to not run link.exe. The /coff option is what tells the compiler to generates a COFF file.
The second line runs the linker. The /subsystem:windows option is required and tells the operating system how to run the executable.
Running the 2 commands will compile and link your MASM assembly to produce a Win32 executable. Note the above will not work if you’re using MASM64.
If you have any problems or questions please leave a comment below.
💡 Assembly Language Weekly Newsletter
Every week I publish a newsletter that contains tutorials, tips, tricks and resources related to assembly language. If you would like to receive this newsletter subscribe here: http://eepurl.com/ghximb
How do you compile assembly code using Visual Studio?
I want to compile and run an assembly source file in Visual Studio 2010.
I’ve created a Visual C++ project and inserted some assembly code in a file code.asm
:
.586 ;Target processor. Use instructions for Pentium class machines
.MODEL FLAT, C ;Use the flat memory model. Use C calling conventions
.STACK ;Define a stack segment of 1KB (Not required for this example)
.DATA ;Create a near data segment. Local variables are declared after
;this directive (Not required for this example)
.CODE ;Indicates the start of a code segment.
clear PROC
xor eax, eax
xor ebx, ebx
ret
clear ENDP
END
However the problem is when you try and compile this, you get:
LINK : error LNK2001: unresolved external symbol _mainCRTStartup
I did go and enable the build customization masm.targets
(right click project > Build Customizations..), but to no avail.
asked Dec 28, 2010 at 19:19
2
Sounds to me like the custom build rules for .asm files isn’t enabled. Right-click the project, Custom Build Rules, tick «Microsoft Macro Assembler». With the «END clear» directive and disabling incremental linking I’m getting a clean build.
It’s different starting from VS2010:
- Right-click Project, Build customizations, tick «masm«.
- Right-click the
.asm
file, Properties, change Item Type to «Microsoft Macro Assembler«.
rustyx
81k26 gold badges200 silver badges268 bronze badges
answered Dec 28, 2010 at 21:41
Hans PassantHans Passant
924k146 gold badges1696 silver badges2536 bronze badges
2
Command line:
Compile the code with:
ml /c /Cx /coff code.asm
You get code.obj as the output.
Link with:
link code.obj /SUBSYSTEM:console /out:go.exe /entry:clear
You can now run go.exe.
Alternatively, do it all in one go with:
ml /Cx /coff code.asm /link /SUBSYSTEM:console /link /entry:clear
Visual Studio (not solved)
answered Dec 28, 2010 at 21:19
bobobobobobobobo
65.1k62 gold badges259 silver badges363 bronze badges
2
Visual Studio includes the MASM macro assembler. Smaller fragments of assembler code are often written in inline assembly in a C or C++ program.
To integrate an assembler file in a Visual Studio project, create a regular C/C++ project (command line or GUI), and just add a file ending in .asm
to the list of source files.
To specify clear as the entry point, follow these instructions:
-
Open the project’s Property Pages
dialog box. For details, see Setting
Visual C++ Project Properties. -
Click the Linker folder.
-
Click the Advanced property page.
-
Modify the Entry Point property.
(It was taken from the Visual Studio documentation.)
I can confirm Hans Passant’s instruction. In addition, according to this article, if you first add the «build customizations» masm checkbox, and then add the file, it will automatically be recognized as an assembler file. Furthermore, not specifying the entry point name in the END directive, but instead specifying it in the project settings also works for me.
answered Dec 28, 2010 at 19:23
Martin v. LöwisMartin v. Löwis
125k19 gold badges198 silver badges235 bronze badges
0
here is how to compile nasm assembly source code with vs20xx:
-
«Excluded From Build» to «No»
-
«Item Type» to «Custom Build Tool»
-
Hit Apply
-
Custom Build Tool -> General -> Command Line:
c:\nasm\nasm -f win64 my_asm.asm
-
Custom Build Tool -> General -> Outputs:
my_asm.obj
-
call the function like this:
extern «C» int foo(void); // written in assembly!
https://www.cs.uaf.edu/2017/fall/cs301/reference/nasm_vs/
nasm tutorial:
http://cs.lmu.edu/~ray/notes/nasmtutorial/
answered Mar 20, 2019 at 6:43
sailfish009sailfish009
2,5711 gold badge24 silver badges31 bronze badges
The problem is that your assembly code is just a function. To compile and link, you need to have a start procedure just like Main
in C/C++. You can specify the start symbol by specifying in your END
directive. Like:
END clear
Or if you want, you can link the .obj file generated with the C/C++ generated .obj one.
answered Dec 28, 2010 at 21:04
Madhur AhujaMadhur Ahuja
22.2k14 gold badges71 silver badges124 bronze badges
3