Ассемблер nasm для windows

Расширенный ассемблер: NASM

Глава 1. Введение

1.1 Что такое NASM?

Расширенный ассемблер NASM √ это 80×86 ассемблер, разработанный исходя из принципов переносимости и модульности. Он поддерживает широкий диапазон форматов объектных файлов, включая форматы Linux a.out и ELF , NetBSD/FreeBSD, COFF , Microsoft 16-bit OBJ и Win32 . Он способен также создавать простые бинарные файлы. Синтакс NASM максимально упрощен для понимания и похож на синакс Intel, но слегка посложнее. Он поддерживает инструкции Pentium, P6 и MMX, а также имеет макро-расширения.

1.1.1 Зачем еще один ассемблер?

Расширенный ассемблер вырос из идеи, поданной на comp.lang.asm.x86 (или возможно на alt.lang.asm — я забыл уже, где), когда по существу не было хорошего свободно доступного ассемблера серии x86 и нужно было, чтобы кто-то написал его.

  • a86 — хороший ассемблер, но не бесплатный, и если вы не заплатите, то 32-битный код писать не сможете — только DOS.
  • gas свободно доступен и портирован под DOS и Unix, но разработан для обратной совместимости с gcc . Поэтому проверка ошибок минимальна, к тому-же, с точки зрения любого, кто попробовал что-либо написать в нем — синтаксис ужасен. Плюс ко всему вы не можете в нем написать 16-разрядный код (по крайней мере, правильно).
  • as86 — только под Linux и (по крайней мере моя версия) кажется не имеет практически никакой документации.
  • MASM очень хорош, очень дорог и работает только под DOS.
  • TASM лучше, но все еще борется с MASM за совместимость, что означает миллионы директив и куча волокиты. Его синтаксис — по существу MASM-овский, но с противоречиями и причудами (в некоторой степени удаляемыми посредством режима Ideal ). Он также дорогой и тоже — только ДОС.

Таким образом, представляем на ваше рассмотрение NASM. В сегодняшнем виде он все еще находится в стадии прототипа — мы не обещаем, что он будет превосходить по быстродействию любой из упомянутых выше ассемблеров. Но пожалуйста, пожалуйста шлите нам замечания о замеченных ошибках, исправления, полезную информацию, да все, что угодно, что вы можете передать нам (и спасибо огромное многим людям, кто уже сделал это!), и мы будем улучшать его (в смысле, NASM) снова и снова.

1.1.2 Условия лицензирования

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

1.2 Контакты

Текущая версия NASM (0.98) поддерживается H. Peter Anvin, hpa@zytor.com. Если вы захотите сообщить об обнаруженных ошибках, прочитайте сначала параграф 10.2.

Связаться с авторами можно по следующим адресам: jules@earthcorp.com и anakin@pobox.com.

Если вы не имеете доступ к Usenet или предпочитаете получать информацию о выпусках новых версий по электронной почте, вы можете подписаться на лист рассылки nasm-announce , послав email по адресу majordomo@linux.kernel.org, содержащий строку subscribe nasm-announce .

Если вы хотите также получать информацию о выходе бета-релизов NASM, пошлите по тому-же адресу (см.выше) письмо, содержащее строку nasm-beta .

1.3 Инсталляция

1.3.1 Инсталляция NASM под MS-DOS или Windows

При получении DOS-архива NASM, nasmXXX.zip (где XXX означает номер версии NASM, содержащегося в архиве), распакуйте его в отдельный каталог (например, c:\nasm ).

Архив содержит четыре исполняемых файла: NASM-исполняемые файлы nasm.exe и nasmw.exe , и NDISASM-исполняемые файлы ndisasm.exe и ndisasmw.exe . Файлы, имеющие в окончании имени w , работают под Win9x/ME/NT, а те, которые без w — работают под DOS-ом.

Для запуска NASM требуются только эти файлы, так что скопируйте их в каталог, указанный в вашей переменной PATH , либо отредактируйте autoexec.bat для добавления пути к каталогу с исполнимыми файлами NASM в переменную PATH . (если вы устанавливаете только версию под Win32, можете смело переименовать nasmw.exe в nasm.exe .)

И это все. NASM установлен! Для запуска NASM не обязательно иметь отдельный каталог (если, конечно вы не добавили его к переменной PATH ), поэтому можете удалить его (каталог), если у вас мало места на диске.

Если вы загрузили DOS-архив с исходниками nasmXXXs.zip , он будет также содержать полный исходный текст NASM и набор Make-файлов, которые вы можете (будем надеяться) использовать для перестроения вашей копии NASM «с нуля». В файле Readme перечислены все Make-файлы и указано, с какими компиляторами они работают.

Обратите внимание, что исходники insnsa.c, insnsd.c, insnsi.h и insnsn.c автоматически генерируются из главной таблицы инструкций insns.dat Perl-скриптом; файл macros.c генерируется из standard.mac другим Perl-скриптом. Хотя дистрибутив NASM 0.98 и включает эти автогенерируемые файлы, вам может потребоваться перестроить их (и следовательно, вам будет необходим интерпретатор Perl), если вы захотите изменить insns.dat, standard.mac или документацию. Возможно в будущем в исходниках не будет этих файлов вовсе. Версии Perl для ряда платформ, включая DOS и Windows, доступны на www.cpan.org.

1.3.2 Инсталляция NASM под Unix

При получении Unix-архива исходников NASM, nasm-X.XX.tar.gz (где X.XX означает номер версии NASM в архиве) распакуйте его в каталог типа /usr/local/src . Архив при распаковке создаст собственный подкаталог nasm-X.XX .

NASM — автоконфигурируемый пакет: как только вы распакуете его, перейдите к каталогу, куда он был распакован и введитеe ./configure . Данный шелл-скрипт найдет самый подходящий компилятор C для сборки NASM и, соответственно, настройки Make-файлов.

Как только NASM сконфигурируется, вы можете ввести make для сборки бинарных файлов nasm и ndisasm , а затем ввести make install для установки их в /usr/local/bin и установки man-страниц nasm.1 и ndisasm.1 в /usr/local/man/man1 . В качестве альтернативы вы можете указать опции типа —prefix к команде configure скрипта (подробности см. в файле INSTALL ) или установить программы самостоятельно.

NASM также имеет набор утилит для обработки заказного формата объектных файлов RDOFF , находящихся в подкаталоге rdoff архива NASM. Вы можете собрать их при помощи make rdf и установить при помощи make rdf_install , если конечно они вам нужны.

Введение в NASM (низкоуровневое программирование для Windows)

«…Абстрактных, ненужных знаний нет. В жизни может случиться, что только знание того, какой чешский король разбил монголов, спасет твою голову…»
В. Короткевич
«Черный замок Ольшанский»

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

Сегодня мы можем наблюдать проникновение микропроцессорной техники практически во все отрасли деятельности человека. Организации осознали выгоду электронного документооборота, вкладывают все большие средства в развитие собственных вычислительных систем и интегрируются в глобальную сеть. Сетевое оборудование должно обладать достаточным «интеллектом», чтобы управлять трафиком и обеспечивать эффективное функционирование сети. Мобильные телефоны благодаря наличию в них мощных вычислительных устройств превратились в тамагочи для взрослых. Да что там говорить — самая последняя кофеварка норовит обзавестись собственным микропроцессором и подключиться не только к электрической, но и к вычислительной сети. Современные микропроцессоры обеспечивают огромную производительность и предоставляют в распоряжение разработчиков постоянно расширяющиеся наборы команд. С одной стороны, это открывает потрясающие перспективы, с другой — накладывает ответственность на специалистов, управляющих их работой. Ведь чтобы реализовать весь потенциал микропроцессора, программист должен применить эффективный алгоритм и воспользоваться при этом самыми подходящими из имеющихся инструкций.

Читайте также:  Windows объединение две сетевых карты

Да, компиляторы с языков высокого уровня обеспечивают такую необходимую сегодня скорость разработки конечного продукта. В большинстве случаев, с которыми сталкивается рядовой программист, ему нет необходимости искать эффективный путь решения задачи — микропроцессор сам «вытянет» это решение, за считанные секунды перебрав миллионы вариантов. Но кому-то придется разрабатывать эти компиляторы, включать в них поддержку инструкций новых процессоров, оптимизировать перевод лексем языка высокого уровня в машинный код. Кто-то должен оптимизировать критические участки кода, чтобы дать возможность остальным позабыть о подобных проблемах и стремиться к «более высоким вершинам». В конце концов, кто-то должен написать первый SDK для нового микропроцессорного устройства, будь то мобильный телефон или кофеварка. Конечно, потребность в таких специалистах намного меньше, чем в программистах «1С» и Visual Basic. Но она будет всегда, пока будут микропроцессоры современного типа. И эти же специалисты пойдут в первых рядах, когда (если) будут изобретены принципиально новые вычислительные устройства. И для тех же прикладных программистов никогда не будет лишним знать, во что превращается высокоуровневый код при его исполнении процессором, и где могут возникнуть «узкие места». Естественно, сразу разобраться с принципами программирования на низком уровне не удастся. Нужно «учиться, учиться и еще раз учиться», а потом приобретать опыт, наступать на грабли, работать над ошибками. И начинать лучше всего в привычной среде, каковой для большинства IT-специалистов является персональный IBM-совместимый компьютер. Кроме того, желательно при разработке программного обеспечения ориентироваться сразу на современную операционную систему, а не на DOS, как это зачастую делается в учебниках.

Возможно ли это — низкоуровневое программирование под Windows? На этот вопрос можно с полной уверенностью заявить: да, и процесс этот можно обставить с достаточным комфортом. Речь в этой статье пойдет как раз о выборе инструментария, организации рабочего места и базовых правилах программирования на ассемблере для 32-разрядных операционных систем семейства Windows. Что же нужно смельчаку, рискнувшему погрузиться в мир низкоуровневого программирования? Если он является убежденным аскетом, то абсолютно ничего, кроме операционной системы и горы технической документации. В самом деле, набрать программу в шестнадцатеричном коде можно даже при помощи текстового редактора. И это не просто бравада автора — на сайте [1] заинтригованный читатель может ознакомиться с лучшими образцами «программистского дзэна». Однако такой подход является скорее искусством и требует огромных затрат времени и сил, обладая при этом очень низким коэффициентом полезного действия. Можно ли сделать низкоуровневое программирование продуктивным? Чтобы разработчик мог не только отвести душу, но и конкурировать на рынке программного обеспечения? Ответить однозначно на эти вопросы сложно. Скорее всего, читатель сам получит ответ, если дочитает эту статью, а потом опробует предложенные рекомендации на практике.

Итак, для продуктивного низкоуровневого программирования прежде всего необходим ассемблер — компилятор мнемонических инструкций в машинный код. Действительно, листинг программы на ассемблере при наличии в нем комментариев и осмысленных меток после некоторой практики читается практически так же легко, как и программа, записанная на языке высокого уровня. Какой же компилятор предпочесть? Чтобы остановить свой выбор на конкретном экземпляре, необходимо рассмотреть хотя бы несколько возможных. На первом месте, естественно, стоит Macro Assembler от Microsoft (MASM).

Большинство приверженцев низкоуровневого программирования для Windows используют его в своих проектах и не имеют претензий. Следует отметить, что Macro Assembler как самостоятельный продукт перестал существовать с версии 6.13, зато появились энтузиасты, которые стали формировать пакеты, отбирая необходимые утилиты из SDK, DDK и Visual Studio. Так появился MASM32 [2] — пакет, содержащий все необходимое для низкоуровневого программирования: собственно ассемблер, компоновщик, библиотекарь и даже пакет документации с большим количеством примеров. К числу положительных черт MASM можно отнести его популярность. Огромное количество макроинструкций и отвлеченных понятий, которые не являются необходимыми для записи алгоритма в понятной для процессора форме, вызывает уважение с одной стороны и вносит путаницу с другой. Кажется, что этот язык можно было бы сделать более лаконичным, однако можно и оправдать наличие некоторых атавизмов требованиями обратной совместимости. Второе место традиционно занимает Turbo Assembler от Borland/ Inprise. В чистом виде в настоящее время этот продукт не существует, однако он входит в состав сред разработчика C++ Builder и Delphi. Это чисто коммерческий продукт — программированием на TASM занимаются энтузиасты, сохранившие верность продукции Borland. По возможностям он сродни MASM. Четко разделить места между остальными компиляторами очень сложно. Поэтому просто рассмотрим еще две оригинальные разработки.

Flat Assembler (FASM) [3] — очень интересный проект, который уже несколько лет ведет Tomasz Grysztar. В отличие от большинства представителей семейства ассемблеров, FASM являет собой в одном лице компилятор и компоновщик. То есть из исходного текста после обработки сразу появляется исполняемый файл или динамическая библиотека. Автор этого продукта постоянно следит за деятельностью фирм — производителей процессоров, поэтому FASM поддерживает инструкции самых последних моделей процессоров Intel и AMD. Кроме того, в FASM реализован лаконичный, но очень мощный макроязык. Существует мнение, что FASM наиболее приспособлен к реализации идей объектноориентированного программирования на ассемблере. На FASM можно программировать как под Windows, так и под Linux. Flat Assembler занимает особое место среди ассемблеров, и поэтому мало подходит для знакомства с традиционной схемой низкоуровневого программирования, при которой исходный текст на ассемблере сначала транслируется в объектный код, а уже файлы с объектным кодом и библиотеки компонуются и записываются в файл, готовый к выполнению.

Netwide Assembler (NASM) [4] был разработан для того, чтобы дать программистам бесплатный инструмент написания программ для Windows. NASM в целом поддерживает синтаксис MASM, причем встречающиеся отличия воспринимаются легко и естественно. NASM оснащен небольшим, но достаточно функциональным набором макроинструкций. По сравнению с FASM NASM консервативен, в нем не находят отражения инструкции новых процессоров, однако благодаря своей традиционности именно он и послужит основой для дальнейшего повествования в этой статье. Пакет NASM включает достаточно подробную инструкцию по установке и использованию. К сожалению, как и большая часть действительно достойной технической документации, она доступна лишь на английском языке. Поэтому, чтобы серьезно заниматься низкоуровневым программированием, потребуется освоить хотя бы базовые элементы английского и найти англо-русский словарь. Если в двух словах, то для установки NASM нужно просто распаковать содержимое дистрибутивного архива в какую-нибудь папку на диске и для удобства прописать путь к этой папке в переменной окружения PATH. Для определенности и по аналогии с традиционной схемой размещения инструментальных программных систем можно рекомендовать построить следующую структуру каталогов (рис. 1), которая встречается в некоторых распространяемых подборках инструментов разработчика NASM для Windows [5]:

Рис. 1. Рекомендуемая иерархическая структура среды разработки NASM

В соответствии с этой схемой в каталоге BIN хранятся все исполняемые файлы среды разработчика (сам компилятор, компоновщик, библиотекарь, отладчик и др.), и путь именно к этому каталогу следует прописать в переменной PATH, чтобы инструменты были доступны при любом текущем каталоге. Каталог DOC предназначен для хранения документации. В каталог INC удобно записать подключаемые модули с описаниями констант Win32 API. После этого, чтобы скомпилировать программу на ассемблере, достаточно набрать в командной строке следующее:

nasmw -p C:\W32NASM\INC\ win32n.inc -f win32 hello.asm

При выполнении этой команды NASM скомпилирует файл hello.asm, предварительно ознакомившись с описаниями констант и типов данных Win32 API, которые хранятся в файле win32n.inc. Результатом компиляции станет объектный файл hello.obj в формате Microsoft Win32 Object File. Более подробную информацию о параметрах командной строки компилятора NASMW.EXE можно получить из сопроводительной документации или запустив его с ключом -h. NASM позволяет создавать объектные файлы как формата Microsoft Win32 Object File, так и Microsoft OMF Object File. В исходных файлах первого типа нельзя использовать директивы import и export, и связь с функциями динамических библиотек может быть осуществлена только с использованием библиотек импорта. Зато предопределены идентификаторы для описания секций .text, .data, .bss. Исходный текст в этом случае может иметь следующую структуру:

Читайте также:  Флешка не может быть прочитана mac os

global main
extern MessageBox
extern ExitProcess

segment .data
.
segment .bss
.
segment .text

main:
.
push dword 0
call [ExitProcess]

Для получения исполняемого файла придется выполнить следующие команды:

C:\> nasmw -f win32 main.asm
C:\> alink -oPE main.obj win32.lib -entry main

Кстати, если при компиляции будет получено сообщение об ошибке типа «attempt to define a local label before any non-local labels», то, скорее всего, в исходном тексте записано нечто вроде «.text» вместо «segment .text».
Если выходной файл должен иметь формат Microsoft OMF Object File, то следует использовать ключ -f obj. В файлах этого типа можно использовать директивы import и export, что позволяет создавать с их помощью библиотеки импорта для DLL-библиотек, которые можно компоновать с win32-файлами. Кроме того, для файлов этого типа предопределена метка ..start, которая отмечает точку входа. Структура исходного файла в этом случае может иметь следующий вид:

SECTION DATA USE32 >.
SECTION BSS USE32 >.
SECTION CODE USE32 >.
..start:
.

и компилироваться он должен так:

C:\> nasmw -f obj main.asm

Традиционная последовательность создания программы на языке ассемблера (опуская такие важные технологические этапы, как постановка задачи, проектирование, тестирование и отладка) состоит из трех основных шагов (рис. 2):

Рис. 2. Этапы создания исполняемого модуля из исходных текстов

Из этой схемы видно, что компилятор нужен только на втором шаге. Как же создать файл с исходным текстом? Использовать «Блокнот»? Можно, конечно, и так, но, чтобы не портить нервы и сразу воспитывать хороший вкус, стоит обратиться к свободно распространяемому продукту ASM Editor for Windows от AV(T) [6]. Что вы получите в этом случае? Кроме приятной цветовой гаммы рабочего пространства — подсветку синтаксиса, настраиваемую контекстную помощь и возможность автоматизировать процесс компиляции. Фактически ASM Editor является интегратором инструментов разработчика, заслуживающим звание IDE. Итак, текст набран и скомпилирован. Как же получить долгожданный EXE-файл? Для этого необходим компоновщик. Для NASM традиционно рекомендуют использовать ALink от Anthony A.J. Williams. Что же, автор этой статьи не имеет возражений против такого предложения. Действительно, ALink прекрасно справляется с компоновкой объектных файлов как формата MS/Intel OMF, так и формата MS-COFF, к Windows-программе без проблем можно подключить необходимые ресурсы. Кстати, о ресурсах. Можно, конечно, было бы обойти этот вопрос стороной, так как программы получаются вполне функциональными и без них. Однако в большинстве случаев использование ресурсов позволяет делать некоторые вещи проще и красивее. Как известно, ресурс может быть описан в обычном текстовом файле. Для преобразования таких файлов в формат, пригодный для компоновки, можно использовать компилятор ресурсов GoRC от Jeremy Gordon. Если же появляется необходимость в правке ресурсов в скомпилированных файлах, то здесь нет равных программе Resource Hacker [7] от Angus Johnson.

Казалось бы, приведен весь перечень необходимых для низкоуровневого программирования инструментов. Но, к сожалению, программы на ассемблере не меньше, а, скорее, больше программ на языках высокого уровня подвержены ошибкам программиста. И для успешной борьбы с этим неприятным явлением необходим мощный инструмент, позволяющий отследить по шагам выполнение программы и найти тот самый проблемный байт. Одним словом, необходим отладчик. В соответствии с договоренностью вести разработку под Windows можно посоветовать Olly Debugger [8] от Oleh Yuschuk. Это действительно стоящая вещь, имеющая встроенный анализатор кода, который графически отображает циклы и переходы. Очень удобен встроенный справочник по функциям Win32 API. Итак, все необходимые инструменты найдены и размещены в каталоге BIN. Можно приступать к обустройству среды разработчика. Как уже было сказано выше, в качестве интегратора будет использоваться ASM Editor. К сожалению, сразу после установки этот пакет настроен на работу с MASM32, но благодаря его гибкости не составит труда эту настройку изменить. Прежде всего нужно спланировать, в каком стиле будет вестись работа. Если предполагается весь исходный текст проекта размещать в единственном ASM-файле — это одно дело. Но такой вариант уже давно признан несостоятельным при реализации больших проектов. Гораздо лучше применить модульный подход, при котором исходные тексты независимых модулей хранятся в отдельных файлах. Автор предлагает оформлять каждый модуль (который условно назовем «name») следующим образом. В файл name.asm записывается исходный текст модуля на языке ассемблера. Так как обычно модуль существует не обособленно, а может обращаться к подпрограммам и областям памяти других модулей, предоставляя, в свою очередь, и им такой же сервис, удобно все идентификаторы импорта-экспорта вынести в файл name.inc. Для того, чтобы не указывать всякий раз параметры компиляции модуля, создается файл name.nsm, в котором описываются подключаемые inc-файлы и параметры, которые обычно указываются в командной строке NASM. Таким образом, каждый модуль проекта будет состоять из трех описанных файлов. Если читатель последует предложенной рекомендации, то для компилирования модуля можно посоветовать написать простой BAT-сценарий следующего содержания:

@echo off
nasmw.exe -E %1.err -@%1.nsm
if errorlevel 1 goto error
goto no_error
:error
notepad.exe %1.err
goto exit
:no_error
del %1.err
:exit

и сохранить его в файле compile.bat в каталоге W32NASM\BIN. После этого, чтобы скомпилировать модуль, достаточно в командной строке набрать:

и в зависимости от корректности исходного текста будет либо сформирован объектный файл name.obj, либо текстовый файл с описанием ошибок, который, к тому же, будет сразу выведен для просмотра в «Блокноте». Более того: если зайти в настройки ASM Editor через меню Service –> Properties… и в таблице вкладки ASM Editor Properties для команды Assemble ASM file записать:

то выполнять компиляцию модуля можно не покидая редактора путем вызова через меню команды Assemble ASM file. Главное — не забыть сохранить перед этим изменения, внесенные в исходный текст модуля, иначе будет скомпилирован старый вариант файла с исходным текстом. На этом подготовительные работы по организации рабочего места можно считать завершенными. Все дальнейшие настройки, способствующие комфортной работе, читатель при необходимости выполнит самостоятельно. Пришло время на простом примере показать технику работы в описанных выше условиях.
Здесь необходимо сделать небольшое отступление и вкратце рассказать о специфике разработки приложений для Windows с использованием ассемблера. Дело в том, что каждая операционная система, предоставляя программисту определенный сервис в решении наиболее общих и востребованных задач, как-то: управление памятью, файловый ввод-вывод и интерфейс с пользователем, устанавливает и определенные правила, которым тот должен следовать при написании программ. Практически любая программа, предназначенная для выполнения в среде операционной системы Windows, использует вызовы функций Win32 API. Поэтому так важно ознакомиться с правилами их использования. Разрабатывая Windows, Microsoft придерживалась рекомендаций Intel 80×86 ABI (Application Binary Interface), в которых как раз и описываются правила взаимодействия модулей программ. Отметим только самые ключевые аспекты этих правил.

В процессе своей работы программы на ассемблере активно используют регистры. Для процессоров семейства i386 это 32-разрядные регистры EAX, EBX, ECX, EDX, ESI, EDI, ESP, EBP (в использовании сегментных регистров необходимость возникает очень редко). В соответствии с правилами ABI функции должны сохранять значения регистров EBX, ESI, EDI и EBP. Значения регистров EAX, ECX и EDX сохранять не обязательно (рис. 3). Важно понимать, что этому же правилу следуют и функции Win32 API. Поэтому после вызова какой-либо из них нельзя предполагать, что значения регистров EAX, ECX и EDX сохранятся. Если планируется их дальнейшее использование, программист должен сам позаботиться об их сохранности, например, задействовав стек.

Читайте также:  Linux dd mbr copy

Рис. 3. Использование регистров в соответствии с ABI

Передача параметров в функции Win32 API осуществляется через стек, причем помещаться в стек они должны в порядке, обратном тому, что можно увидеть в описании функций. За редким исключением (например, очень удобная функция wsprintf) функции Win32 API сами очищают стек от параметров. Подавляющее большинство, если не все функции Win32 API в качестве параметров принимают 32-битные значения. Если значение параметра не укладывается в указанные границы, используется 32-битный указатель. Функции Win32 API возвращают результат своей работы через регистр EAX. Рассмотрим на примере, как должен осуществляться вызов функции MessageBox, чтобы вывести диалоговое окно с сообщением pszMessage и заголовком pszCaption (предполагается, что владельцем диалога будет окно, манипулятор которого сохранен в 32-битной переменной hwnd):

.
push dword MB_ICONINFORMATION | MB_OK
push dword pszCaption
push dword pszMessage
push dword [hwnd]
call [MessageBox]
.

Здесь нужно заметить, что ассемблер NASM не отслеживает типов переменных даже если они описаны с указанием размера выделяемой памяти. Поэтому в операциях, не затрагивающих регистры, необходимо явное указание на размер операндов — в данном случае dword. Так как в программах для Windows вызовы функций Win32 API встречаются сплошь и рядом, использовать такие громоздкие описания было бы затруднительно. Поэтому хорошим выходом из сложившейся ситуации будет разработка макроса, позволяющего записывать вызов в одну строку. Автор решил использовать синтаксис, подобный обращению к подпрограмме в языках семейства DBASE IV. Макрос для этих целей записывается так:

%imacro DO 1-*
%rep %0 — 1
%rotate -1
push dword %1
%endrep
%rotate -1
call [%1]
%endmacro
%idefine WITH,

а так выглядит вызов функции MessageBox:

DO MessageBox WITH [hwnd], pszMessage, pszCaption, MB_ICONINFORMATION | MB_OK

Как уже было сказано, для NASM существует готовая библиотека импорта функций Win32 API. Однако подобную библиотеку можно создать и самостоятельно. Для этого в NASM имеются директивы extern и import. Их описание приведено в сопроводительной документации, а в этой статье рассмотрим два макроса, которые позволяют оформлять исходные тексты библиотеки импорта в более читаемом виде:

%macro DLL 1
%define __DLL %1
%endmacro

%imacro FUNCT 1-2
extern %1
%if %0 = 1
import %1 __DLL
%else
import %1 __DLL %2
%endif
%endmacro

Эти макросы удобно сохранить в файле import.inc и подключать его при описании библиотек импорта. Используются они следующим образом:

DLL KERNEL32.DLL
FUNCT ExitProcess
FUNCT GetCommandLine, GetCommandLineA
.
DLL USER32.DLL
FUNCT MessageBox, MessageBoxA
FUNCT CreateWindowEx, CreateWindowExA
.
.
Как видно из этого простого примера, макрос DLL начинает блок описания функций, импортируемых из определенной динамической библиотеки. Существенно то, что имя DLL-файла обязательно должно указываться с расширением, иначе программа, вполне работоспособная в Windows 9x/Me, откажется запускаться под Windows NT/2000. Макрос FUNCT служит для описания импортируемых функций. Первый его параметр задает идентификатор функции в том виде, в каком он будет использоваться в программе, а второй соответствует идентификатору, записанному в самой DLL-библиотеке. Если оба идентификатора совпадают, второй параметр можно опустить.

Теперь пришло время продемонстрировать пример простой программы, которая будет выводить на экран диалоговое окно с содержимым командной строки. Сначала нужно набрать исходные тексты макросов DLL, FUNCT, DO и WITH, которые приведены выше, и сохранить их в файле import.inc. Затем следует создать файл import.asm, в котором описать используемые в примере функции Win32 API следующим образом:

DLL KERNEL32.DLL
FUNCT GetCommandLine, GetCommandLineA
FUNCT ExitProcess
DLL USER32.DLL
FUNCT MessageBox, MessageBoxA

Теперь в соответствии с тем, что говорилось о разработке модульных программ, нужно связать INC- и ASM-файлы, создав файл import.nsm следующего содержания:

import.asm
-p import.inc
-f obj

Можно попробовать выполнить компиляцию этого файла так, как было описано выше. В результате должен получиться файл import.obj, который позволит разрабатываемой программе обращаться к описанным функциям Win32 API. Исходный текст самой программы нужно записать в файле test.asm:

segment .data
szCaptiondb «The command line is. «

DO GetCommandLine
DO MessageBox WITH 0, eax, szCaption, 0

xor eax, eax
DO ExitProcess WITH eax

а в файл test.inc поместить описания экспорта-импорта в следующей форме:

extern MessageBox
extern GetCommandLine
extern ExitProcess

Увязать оба созданных файла, как и в предыдущем случае, следует с помощью файла test.nsm следующего содержания:

test.asm
-p test.inc
-p import.inc
-f win32

После того, как оба модуля, IMPORT и TEST, будут успешно скомпилированы, т.е. появятся объектные файлы import.obj и test.obj, можно переходить к созданию исполняемой программы с помощью следующей команды:

C:\> alink test.obj import.obj -oPE -entry start]

Если все было проделано правильно, то в каталоге появится файл test.exe, который можно запустить. Если в командной строке ничего не указывать, на экран будет выведено окно с полным путем к EXE-файлу. Если при запуске написать в командной строке какой-нибудь текст, он будет выведен на экран следом за путем к файлу. Так работает функция GetCommandLine.

В заключение обобщим то, о чем шла речь в этой статье. Вначале была рассмотрена область применения микропроцессорной техники, чем аргументирована важность навыков низкоуровневого программирования. Было предложено осуществить знакомство с этим процессом в привычной операционной среде IBM-совместимого компьютера. Далее в целях облегчения старта для новичков в области программирования был рассмотрен спектр инструментальных средств и предложен конкретный набор, включающий в себя компилятор исходных текстов и ресурсов, компоновщик объектных и библиотечных файлов, отладчик и текстовый редактор. Были даны рекомендации по технике модульного программирования, рассмотрены основные правила использования функций Win32 API в программах на ассемблере, приведен пример простейшего приложения, разработанного с учетом предложенных рекомендаций. Если читатель дошел до этих последних строк статьи, он уже может сделать предварительный вывод о том, насколько продуктивно заниматься низкоуровневым программированием под Windows. В любом случае стоит отметить тот замечательный факт, что большинство инструментов, описанных в этой статье, являются абсолютно бесплатными и могут легально использоваться в том числе и в коммерческих разработках.

Конечно, в одной статье невозможно рассказать обо всех аспектах низкоуровневого программирования. Заинтересовавшемуся читателю придется перечитать многие страницы технических руководств и других статей, прежде чем он будет вознагражден реально работающей программой собственной разработки. Можно посоветовать ознакомиться со статьями Iczelion [9], в которых подробно описаны теория и практика программирования на ассемблере под Windows (русский перевод можно найти на [1]). Обязательно придется обзавестись справочником по функциям Win32 API (файл win32.hlp входит в комплект поставки сред разработки — например, Borland C++ 5.0). Ну, и никуда не удастся уйти от изучения работы микропроцессора и управляющих им инструкций, которые можно найти на сайтах производителей микропроцессоров. Стоит ли полученный результат затраченных усилий? Вопрос риторический, и ответ на него в большой степени зависит от таланта и усердия разработчика. В любом случае полученные навыки и умения могут оказаться полезными в самой неожиданной ситуации, которыми в избытке одаривает динамичная современная жизнь программистов.

Игорь Орещенков, 2004

Компьютерная газета. Статья была опубликована в номере 50 за 2004 год в рубрике программирование :: разное

Оцените статью