Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Textnew2

.pdf
Скачиваний:
13
Добавлен:
06.02.2018
Размер:
1.48 Mб
Скачать

push dword txt ; address of txt

push dword [hstdout] ; N handle=hstdout call WriteFile

;--- WriteFile(hstdout, symbl, 1, &actlen, NULL) push dword 0

push dword actlen

push dword 1 ; number of bytes push dword symbl ; address of txt

push dword [hstdout] ; N handle=hstdout call WriteFile

; ExitProcess(0) push dword 0

call ExitProcess

SEGMENT .data USE32 txt db 'Privet!'

symbl db '$' hstdout dd 0 actlen dd 0

STD_OUTPUT_HANDLE equ -11

Рис. 5.4.1. Простейшая программа wprim.asm вывода текста в ОС Windows

Для превращения этой исходной программы на языке ассемблера в исполняемую программу можно воспользоваться следующими двумя строками командных вызовов:

nasmw -f obj wprim.asm -l wprim.lst

alink -oPE -subsys console wprim C:\util\win32.lib

При разработки многих программ для Windows на ассемблере целесообразно использовать командный файл, текст которого приведен на рис. 5.4.2.

nasmw -f obj %1.asm -l %1.lst

alink -oPE -subsys console %1 C:\util\win32.lib

Рис. 5.4.2. Командный файл для построения исполняемых файлов из NASM-ас- семблерных

Его применение заключается в вызове по имени этого командного файла, за которым через пробел следует собственная часть имени файла на ассемблере. В

91

частности, при наименовании этого командного файла как asmal.cmd, такой вызов для предыдущего примера будут задаваться строкой

asmal wprim

(Заметим, что в ОС типа Windows 9x, в отличие от Windows NT - Wimdows2000 и т.п., расширение командного файла должно быть не cmd, а bat).

Для получения исполняемого файла, предназначенного выполняться в 32-бит- ных операционных системах от MS, следует использовать опцию компоновщика alink.exe, имеющую вид -oPE. Причем для исполняемых файлов текстового режима (называемых здесь консольными приложениями) следует дополнительно указывать в качестве рабочей подсистемы именно консольный режим, задавая его опцией

-subsys concole

Кроме того, компоновщик alink.exe для рассматриваемых исполняемых файлов должен использовать предназначенную для него библиотеку импорта, которая в указанном комплекте имеет имя win32.lib. (Особо подчеркнем, что следует использовать библиотеку импорта именно из указанного комплекта, а не просто "где-то найденный" файл с именем win32.lib) Эту библиотеку при вызове компоновщика нужно указывать как один из компонуемых файлов, в частности задавая его полным именем.

В рассмотренных примере и варианте командного файла предполагается, что библиотека импорта размещается в каталоге C:\util, при размещении ее в другом месте следует (вместо использованного здесь имени каталога) записать действительное имя каталога, хранящего ее. (Еще одной возможностью является указание каталога, в котором помещаются библиотеки импорта, с помощью дополнительной опции -L, что в данном примере приводит к командной строке внутри командного файла, имеющей вид

alink -oPE -subsys console %1 -L C:\util win32.lib

Вариации последней возможности целесообразно использовать, когда разработчику при вызове компоновщика alink нужно указать более чем одну библиотеку импорта в одном каталоге. Тогда, вместо задания полного пути к каждой библиотеке, можно его указать один раз для всех этих библиотек. Заметим еще, что при размещении библиотеки импорта win32.lib в текущем каталоге, откуда производится вызов командного файла, путь к этой библиотеки можно не указывать - ни опцией -L, ни прямой записью пути в полном имени этого файла.)

Если, по какой-нибудь причине, имеется желание использовать компоновщик TLINK32.EXE, то его удобно использовать для наших целей в составе командного файла asmtl.cmd, состоящего из строк

nasmw -f obj %1.asm -l %1.lst tlink32 %1, , , IMPORT32

Рис. 5.4.3. Командный файл asmtl.cmd, использующий компоновщик tlink32

Этот вариант компоновки использует библиотеку импорта IMPORT32.LIB для системных библиотек, входящую в состав систем разработки Borland/Inprise. Библиотека используется со своего места размещения в указанной системе разработке,

92

если последняя установлена нормальным образом. В простейшем случае отсутствия полной установки системы разработки достаточно использовать только исполняемый файл TLINK32.EXE и файл IMPORT32.LIB, переписав их, например, в текущий каталог для программирования на NASMW.

5.5. Особенности использования объектных файлов формата PE

Кроме формата OBJ для объектных модулей, предназначенных для Windows, используется формат, введенный позже фирмой Microsoft исключительно для 32-бит- ных приложений, и названный вначале ею как COFF (Common Object File Format). В действительности этот формат был разработан и усовершенствован при создании классических версий ОС UNIX, а Microsoft только адаптировала его к своим нуждам. Несмотря на приемственность, полученный формат PE - Portable Executable, предназначенный для исполняемых файлов, объектного кода и динамических библотек, заметно, хотя и в деталях отличается от COFF. Заметим, что ведущая идея COFF была в использовании единого формата как для объектных, так и для исполняемых файлов. Основное отличительное свойство PE в том, что запуск такого исполняемого файла в однозадачной операционной системе Microsoft или в 16-бит- ной версии MS Windows приводил к квазизапуску примитивной внутренней вставки в исполняемый файл, отвечавшей требованиям указанных предыдущих систем, которая все лишь выдавала сообщения, что не может предлагаемый файл формата PE запустить как нормально функционирующее приложение. Обычно сообщалось, что «This program must be run under Win32». Таким образом, едва ли не основной заботой разработчиков Microsoft при разработке формата PE было обеспечение обратной совместимости — чтобы в старых версиях их операционных систем происходили бы понятные действия с новыми программами, не предусмотренными ранее в них, и это являлось следствием прежней непредусмотрительности, которая заключалась в отсутствии заложенных в эти устаревшие системы действий на нестандартный формат исполняемого файла.

В обозначениях NASM формат PE задается служебным сокращением win32. Для получения из такого формата исполняемых файлов следует применять компоновщик Microsoft или совместимые с ним.

Особенностью последнего варианта из-за соответствующих компоновщику библиотек доступа к ОС являются специфические имена системных функций API, хотя и подобные системным именам прототипов из документации для языка Си, но все же заметно отличные от них. Это отличие заключается, что исходное имя от прототипа на Си дополняется символом '@', за которым записывается число байтов в области аргументов вызова этой функции (это число можно посчитать исходя из информации прототипа и заголовочных файлов или использовать дополнительную информацию из заголовочных файлов специально для ассемблерных программ). Кроме того, перед исходным именем из прототипа приписывается дополнительный символ '_' (символ подчеркивания).

93

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

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

На рис. 5.5.1 приведена программа на NASM ассемблере для результирующего формата win32 объектных файлов. Причем для разнообразия в ней решается задача ввода строки текста, изменение в ней второго из введенных на символ ! и вывод полученной строки.

GLOBAL _start

EXTERN _GetStdHandle@4, _WriteFile@20, _ExitProcess@4 EXTERN _ReadFile@20

SEGMENT .text

_start:

push dword STD_OUTPUT_HANDLE call _GetStdHandle@4

mov [hstdout],eax

push dword STD_INPUT_HANDLE call _GetStdHandle@4

mov [hstdin],eax

;--- ReadFile(hstdin, buf, 80, &actlen, NULL) push dword 0

push dword actlen

push dword 80 ; number of bytes push dword buf ; address of txt

push dword [hstdin] ; N handle=hstdout call _ReadFile@20

 

mov byte [buf+1],'!'

 

; или двумя командами:

mov ebx, buf ;

;

mov byte [ebx+1], '!'

;--- WriteFile(hstdout, buf, actlen, &actlen, NULL)

 

push dword 0

 

 

push dword actlen

; address of actual byte number

94

push dword [actlen] ; number of bytes (value !) push dword buf ; address of txt

push dword [hstdout]; N handle=hstdout call _WriteFile@20

; ExitProcess(0) push dword 0

call _ExitProcess@4

SEGMENT .data buf times 80 db 0 hstdin dd 0

hstdout dd 0 actlen dd 0

STD_INPUT_HANDLE equ -10 STD_OUTPUT_HANDLE equ -11

Рис. 5.5.1. Программа для формата win32 объектного файла Windows

Для превращения программы NASM в объектный файл формата win32 и изготовления из последнего исполняемой программы можно воспользоваться командным файлом с текстом, приведенным на рис. 5.5.2

nasmw -f win32 %1.asm -l %1.lst

link /ENTRY:start /SUBSYSTEM:CONSOLE %1.obj kernel32.lib

Рис. 5.5.2. Командный файл для получения программы Windows через формат win32 c использованием link.exe

Использование предложенного вариант требует наличия доступных компоновщика фирмы MS с именем LINK.EXE и библиотеки импорта KERNEL32.LIB, предназначенной для него, а также библиотеки MSPDB60.DLL (так называемой библиотеки динамической компоновки или, иначе, библиотеки совместно используемых при выполнении объектов).

Заметим, что свободно распространяемый компоновщик alink.exe, о котором рассказывалось выше, не позволяет использовать библиотеки импорта в формате COFF (в частности библиотеку KERNEL32.LIB), хотя сам этот формат для объектных файлов и библиотек объектных файлов данный компоновщик поддерживает. При использовании этого компоновщика приходится применять ту же библиотеку импорта win32.lib, которая обсуждалась выше, и организовывать доступ к системным функциям через эту библиотеку, т.е. в соглашениях, отличных от COFF. При этом в исходных текстах программ ассемблера придется использовать обозначения системных функций без характерных для библиотек импорта COFF префиксов и

95

суффиксов (что программисту может показаться не только не худшим, а предпочтительным вариантом). К сожалению, такой компромиссный подход делает невозможным использовать объектные файлы формата COFF, созданные транслятором из MS Visual C или подобным ему. Следует учитывать, что достаточно характерным использованием ассемблера оказываются разработки, в которых часть объектных файлов формируется на языке высокого уровня, а только часть - пишется собственно на ассемблере. Эта причина заставляет разбираться с особенностями программирования для различных видов объектных файлов и соблюдать в таких разработках общие форматы построения объектных файлов.

С учетом широкого распространения COFF, вынуждаемых нередко используемой системой разработки программ, рассмотрим два вспомогательных приема, позволяющих использовать стандартную форму обращения к системным функциям ОС MS Windows. Оба они являются вариациями автоматизированного формирования имен системных функций с префиксами и суффиксами от MS. Это формирование выполняется на основе обычного имени системной функции, взятого из документации по программному интерфейсу программ (API).

Первый из приемов заключается в использовании специальных файлов, имеющих обычно расширение INC и содержащих фактически переименование от простого наименования к полному. Например, для доступа к системной функции GetStdHandle, имеющей полное имя _GetStdHandle@4 в COFF, используется директива ассемблера

#define GetStdHandle EQU _GetStdHandle@4 совместно с директивой

extern _GetStdHandle@4

Применение таких файлов, содержимое которых включается, в свою очередь, в исходный текст разрабатываемой программы директивой

#include имя специального файла вспомогательных описаний

позволяет в тексте ассемблерных программ по прежнему писать простое имя системной функции, но это имя будет заменяться для объектного файла требуемой формой полного имени [5].

Второй подход, используемый в примерах инструментального пакета NASMW, основан не применении специализированно макрокоманды с именем sc, содержащейся в вспомогательном файле windows.inc (также содержащегося в этом пакете). Указанная макрокоманда (вместе с другими, обеспечивающими ее функционирование) используется в тексте ассемблерной программы, чтобы в конкретном месте сформировать обращение по полному имени. Более того, она содержит внутренние средства, позволяющие аргументы вызова писать прямо в качестве аргументов этой макрокоманды (вместо того, чтобы непосредственно записывать все команды укладки аргументов в стек перед вызовом системной функции). Например вызов функции GetStdHandle для получения хэндла стандартного вывода записывается в виде

sc GetStdHandle, STD_OUTPUT_HANDLE

96

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

Вместо компоновщика LINK.EXE от Microsoft для компоновки объектных модулей формата COFF совместно с библиотеками импорта этого же формата может быть использован компоновщик фирмы Sybase с именем wlink.exe, входивший в состав системы программирования Watcom C++. Указанная система программирования широко функциональная, что проявляется в возможности строить с ее помощью исполняемые файлы для большого числа операционных систем. Побочным следствие такой широты возможностей стало обилие аргументов вызова программы компоновщика, которые могут быть заданы при компоновке. В данном контексте мы рассмотрим вариант построения исполняемого файла только для современных версий операционных систем типа Windows.

Врассмотренном выше примере - для компоновки объектного файла - в составе командного файла достаточно воспользоваться вызовом вида

wlink File %1.obj FORMat window nt RUntime console OPtion START=_start Library kernel32.lib

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

Для задания исполняемых файлов формата Portable Executable (PE) синтаксис компоновщика WLINK требует указания ключевого слова FORMAT, которое может быть сокращено до его начальной части FORM, и условного обозначения 32битных систем MS Windows, которое в данном компоновщике задается как windows nt. (Обозначение единственным словом windows было предназначено для 16-битной предшественницы современных MS Windows.)

С помощью аргумента RUNTIME выбирается будет ли программа выполнятся в среде графической оболочки или в консольном режиме. Во втором случае используется значение CONSOLE, а в первом (которое данное изложение не применяет) - значение WINDOWS. Аргумент, задающий опции, указывается ключевым словом OPTION, позволяющим сокращать его до двух первых букв. В качестве значения этого аргумента используется, в свою очередь единственный необходимый для те-

97

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

START=имя_точки_входа

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

С учетом замечаний, рассматриваемая командная строка может быть сокращена

до

wlink File %1.obj FORM window nt RU console OP START=_start L kernel32.lib хотя в этом случае она труднее воспринимается программистом, если последний лишь эпизодически использует что-то подобное. Другим вариантом сокращения записи вызова компоновщика является использования косвенного вызова. В этом случае все аргументы, используемые достаточно часто, записываются в отдельный файл, например с именем файларг, а в самом вызове компоновщика использование содержимого этого файла указывается в виде @файварг.

Например, для наших текущих целей может быть построен вспомогательный файл с именем aline.lin, помещенный в каталоге C:\UTIL и содержащий строки

FORMat window nt

RUntime console

OPtion

START=_start

Library kernel32.lib

а вызов компоновщика быть записан в самом командном файле как wlink F %1.obj @C:\UTIL\aline.lin

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

nasmw -f win32 %1.asm -l %1.lst wlink F %1.obj @C:\UTIL\aline.lin

Рис. 5.5.3. Командный файл для получения программы Windows через формат win32 с использованием wlink.exe

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

98

5.6.Стандартный доступ к системным функциям Unix

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

Для ОС Unix, реализованных в архитектуре Intel, стандартный доступ выливается в укладку аргументов на стек и вызов машинной функции CALL, конкретизация же машинного доступа осуществляется с помощью стандартной библиотеки языка С, которая оказывается необходимой при компоновке.

Сама стандартная библиотека языка С в Linux называется libc и размещается в каталоге, который автоматически просматривает компоновщик (информация о таких каталогах находится в конфигурационном файле /etc/ld.conf ).

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

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

Он требует явного указания в составе опций вызова программы ld опции -dynamic-linker полное_имя_динамического_компоновщика

где полное имя динамического компоновщика в момент написания данного пособия в Linux задается как /lib/ld-linux.so.2, обозначая разделяемую библиотеку. (В общем случае, если такого файла в системе разработки нет, целесообразно либо найти его близкий аналог по названию и попробовать его использовать, либо, что значительно надежней, найти какое имя динамического компоновщика используют выполняемые файлы, получаемые как результат работы программы gcc.) Указанная информация относиться к 32-битным версиям Linux.

Для 64-битной версии полное имя динамического компоновщика задается как /lib64/ld-linux-x86-64.so.2, причем в общем случае имеет смысл проверить непосредственно по каталогам текущей установленной версии Linux, что именно этот исполняемый файл действительно есть в наличии и может быть далее использован при запуске программы. Заметим, что неправильное имя этого компоновщика, фиксируемое в исполняемом файле, приводит к тому, что при попытке запуска исполняемой программы, в простейшем случае, запуске ./a.out выдается сообщение

bash: ./a.out: Нет такого файла или каталога

Указанной сообщение (в руссифицированной версии) может вызвать недоумение у изучающего программирование, с учетом того, что указанный файл в текущем каталоге есть, но обозначает по существу отсутствие не собственно его, а от-

99

сутствие необходимого динамического компоновщика в указанном месте или само отсутствие указания компоновщика в исполняемом файле.

Кроме того, при компоновке необходимо указать использование самой библиотеки libc, для чего служит специальная опция вида -lc. (Собственно опция, в общем случае имеет вид -lсобственная_часть_имени_библиотеки, а первые три буквы обозначения библиотеки libc относятся к стандартной части ее наименования.)

Поэтому для построения выполняемой программы из одного исходного файла программы на ассемблере целесообразно использовать командный файл, текст которого приведен на рис. 5.6.1.

nasm -f elf $1.asm -l $1.lst

ld -o "$1".exe "$1".o -dynamic-linker /lib/ld-linux.so.2 -lc

Рис. 5.6.1. Командный файл nasmlc для трансляции программ со стандартным доступом

Второй из упомянутых подходов оказывается значительно проще. Он предполагает использование для компоновки командного вызова вида

gcc имя_объектного_файла.o

Дело в том, что программа gcc настолько универсальна, что из объектного файла (или объектных файлов), указанного в перечне ее параметров, она формирует исполняемый файл, автоматически выполняя все необходимые при компоновке действия. При этом следует иметь в виду только одну, но очень важную деталь: точка

входа в исходном ассемблерном файле должна быть обозначена не именем _start, а именем main. Для использования gcc с указанными целями может быть также построен командный файл, изображенный на рис. 5.6.2.

nasm -f elf $1.asm -l $1.lst gcc -o "$1".exe "$1".o

Рис. 5.6.2. Командный файл nasmlg для трансляции программ со стандартным доступом

Программы стандартной библиотеки языка Си в Linux вызываются, естественно, по стандартным для этого языка соглашениям. В частности, для программ на языке ассемблера необходимо позаботиться о очистке участка стека от использованных аргументов вызова. Напомним, что для этого после выполнения команды CALL имяфункции записывается команда

ADD ESP,длина_области_аргументов.

К удобству пользователей ОС Unix даже на ассемблере имена функций библиотеки языка Си не требуют никаких дополнительных символов. При этом не следует забывать указывать эти имена и в директивах EXTERN программы на ассемблере.

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

100

Соседние файлы в предмете Операционные системы