IT-Блог о программировании и операционных системах

Скелет программы на ассемблере

Автор: Goppit / ARTeam [www.accessroot.com]
Перевод: Rob [www.delphiday.blogspot.com]
Номер тьюториала: #1, Basic Assembly Program Skeleton.

Использование WinAsm

На следующем скриншоте запечатлёна часть интерфейса среды WinASM. Исходный код, который вы набираете, появляется в левой части среды – в панели исходного кода. Панель обзора отображает файлы, которые в свою очередь являются частью текущего проекта (файлы исходного кода - *.asm, инклудники - *.inc, файлы ресурсов, и т.д). Кнопка “Go All” (на скриншоте указана курсором мыши) соберёт, слинкует и запустит на выполнение результирующий exe файл. Комманды ассемблера и линковщика с результатами компиляции и компоновки вы можете лицезреть в нижнем окне – в панели вывода.




Эти семь строк кода – ни что иное, как минимальное приложение, которое будет скомпилировано, слинковано, и запущено. Оно содержит всего одну ассемблерную инструкцию RET в точке входа (Entry Point, EP), которая, при выполнении передаёт управление ОС Windows. Для того чтобы сделать данный шаблон полезным при написании программ, мы изменим эту инструкцию, на call ExitProcess – стандартная функция API которая завершает приложения.

Для того чтобы начать разрабатывать программу, откройте WinASM и кликните на самую первую кнопку на панели. Заметьте, что я отключил штатный менеджер проектов, и вместо него использую дополнение “New Project Wizard Add-in”, которое очень расширяет базовую функциональность. Если вы решили настроить свою среду так же как я, вы будете видеть следующее окно:


Нажмите Next, затем Finish. Проект стандартной EXE программы, который выделен по умолчанию, автоматически устанавливает настройки ассемблера и компоновщика таким образом, чтобы на выходе получить Win32 GUI программу. WinASM спросит вас о сохранении нового файла проекта и нового ASM-файла. После этого вы увидите пустой редактор исходного кода.

Теперь вставьте skeleton.asm из секции исходных кодов этого файла (прим. Пер. Эталонный документ собран в один chm архив, в котором присутствуют инструменты, сорцы и даже картинки c чиптунами  ) в редактор кода. Соберите проект, нажав на Go All. Сначала среда снова спросит вас о сохранении изменённого исходника, в существующий путь, затем произойдёт компиляция сборка и линковка проекта, ну а в след за ними и сам запуск нового приложения. На этом этапе, код, который вы ввели в редактор должен быть таким:


Рассмотрим подробнее код приложения строка за строкой. Большинство из этих инструкций представляют собой директивы (инструкции для компилятора). Вобщем то здесь только одна строка программного кода – вызов API функции ExitProcess.

.386 – Указывает ассемблеру MASM использовать инструкции процессора 80386.

.model flat, stdcall – устанавливает плоскую модель память и стандартное соглашение о вызовах процедур.

option casemap:none – указывает на то, что метки не должны быть регистрозависимы.

include kernel32.inc - говорит ассемблеру включить в проект инклудник, как если бы мы добавили его к тексту проекта вручную, в это место, где находится директива.

includelib kernel32.lib – указывает компоновщику, какие библиотеки импорта подключать к проекту.

.data – описывает секцию инициализированных данных (переменные, которым присвоено начальное значение)

.data? – секция неинициализированных переменных (память для них выделяется при старте программы, и размер файла на диске не увеличивается в отличии от переменных, описанных в секции data).

.const – описывает секцию констант.
.code – указывает, что основной исполняемый код будет идти с этого места.
start: - метка начала кода (заметьте, что при объявлении метки, в конце ставится запятая, а при её использовании – нет)

invoke ExitProcess,0 – выполнение кода начинается здесь, сразу за меткой. Данная инструкция выполняет вызов API функции, которая завершает текущий процесс.

end start – обозначает конец модуля и устанавливает точку входа программы за назначенной меткой start (заметьте, что знак “:” здесь не ставится).

Интерфейс Win32 API

Windows API (Application Programming Interface) включает в себя необходимую коллекцию типов данных, констант, функций и структур, которые используются для разработки приложений под управлением операционной системы Windows. Большинство функций API, включая и функцию ExitProcess, которую мы использовали ранее, находятся в трёх главных динамических библиотеках (DLL):

• KERNEL32.DLL – Низкоуровневые функции ядра.
• GDI32.DLL – Интерфейс графических устройств: Вывод графики и печать
• USER32.DLL – Элементы пользовательского интерфейса, окна и сообщения.

Если вы используете функции API в своих программах, то ваше приложение импортирует их из соответствующих динамических библиотек. Библиотеки импорта (файлы *.lib) содержат информацию для компоновщика, которая нужна ему для обращения к функциям из DLL. Так система сможет загрузить нужную динамическую библтотеку и отыскать экспортируемую функцию, нужную для работы вашей программы. Например, чтобы вызвать функцию ExitProcess, которая находится в библиотеке kernel32.dll, вы должны добавить к своему коду библиотеку импорта kernel32.lib. Но одной библиотеки импорта будет недостаточно. Вместе с ней вы должны подключить к проекту и kernel32.inc.

Инклудники (.inc) включают в себя прототипы функций, которые описывают параметры всех функций из одноимённой динамической библиотеки (.DLL). Они могут быть автоматически созданы с помощью специальной утилиты – lib2inc. Лёгких путей создания библиотечных файлов из DLL не существует, но большая часть API функций которые вам понадобится вызывать из DLL уже содержат нужные для этого библиотечные файлы, поставляемые вместе с пакетом MASM.

Официальная документация по Win32 API написана для Си и Си++ программистов и обычно, в такой документации все функции объявлены следующим образом:

ВозвращаемыйТип FunctionName ( ТипПараметра ИмяПараметра1, ТипПараметра2 ИмяПараметра2,...);

Приведу пример описания функции SetWindowText из справки Win32 Programmers Reference:


Синтаксис языка Си очень легко перевести на ассемблер. Слово BOOL – это тип данных, данные этого типа функция возвращает в качестве своего результата. HWND также представляет тип данных, а также их размер, которые передаются функции (на самом деле всё это тип DWORD – все эти типы описаны в файле windows.inс в директории MASM\include). Второе слово – это имя данных (переменной, константы).

Гораздо проще функции вызываются в ассемблере – переменные или константы, которые нужно передать функции, сначала вталкиваются в стек (PUSH) и затем по имени вызывается сама функция (CALL):
PUSH  lpString
PUSH  hWnd
CALL   SetWindowText
Заметьте, что в ассемблере, в отличии от Си, параметры вталкиваются в стек в обратном порядке. Это соглашение STDCALL, которое в основном (прим. Пер.:а впрочем и всегда) и используется в ассемблере.

Использование invoke

Добавление прототипов в исходный код программы даёт два приятных преимущества в MASM.
Первое – это то, что инструкция invoke может быть использована вместо традиционной последовательности из PUSH и CALL. Код где использован invoke более компактен на вид, а так же, нет необходимости заботиться о том, что параметры должны вталкиваться в стек в обратном порядке.
Invoke SetWindowText,hWnd,lpString
Второе преимущество заключается в том, что invoke позволяет проводить проверку типов, так что если вы пытаетесь передать в функцию другой тип (заведомо неверный), то компоновщик оповестит вас об этом сообщением от ошибке.

Вы так же можете объявлять прототипы собственных функций и вызывать их с помощью invoke, но для этого необходимо, чтобы прототип был объявлен раньше, чем будет использован вызов функции с помощью макроса invoke.

Комментариев нет:

Отправить комментарий