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

Textnew2

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

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

MOV edx, число

Такой же по виду командой в регистр eax (перед вызовом программного прерывания) заносится системный номер базовой системной функции.

Для наглядности перед командами реализации вызова системной функции приведен ее текст на языке Си, но записанный в виде комментариев. Причем для удобства усвоения запись вызова функции на языке Си сопровождается условной записью вида <номер_функции>(ebx, ecx, edx) или подобной, но с другим числом аргументов в скобках. Последняя форма записи напоминает об используемых в Linux правилах вызова базовых системных функций.

2.3. Средства преобразования в исполняемый файл

Текст программы на рис. 2.2.1 и подобные ему сами по себе не способны управлять компьютером, для такого выполнения их необходимо преобразовать в исполняемый файл. Такое преобразование осуществляется транслятором с языка ассемблера. Этот транслятор является исполняемым файлом соответствующей операционной системы и имеет в Linux имя nasm. В ОС Windows именем транслятора ассемблера NASM является nasmw.exe. Эти трансляторы предназначены для использования в режиме командной строки (не интегрированной системы разработки) и управляются, в свою очередь, опциями командной строки.

Наиболее важной из этих опция является опция формата для файла результата. Задается эта опция в командной строке в виде

-f формат

где параметр формат может принимать ряд символических значений, из которых в данном курсе представляют интерес значения bin, obj, elf и win32. Формат bin указывает порождение простейшего варианта двоичного кода машинных команд и данные без какой-либо служебной или настроечной информации. Этот вариант используется для построения простейших программ для MS-DOS (так называемых com-файлов, представляющих собой исполняемые файлы односегментных программ) и, кроме того, для построения драйверов этой же ОС (файлов со стандартными расширениями sys).

Формат obj отвечает варианту построения объектных файлов, которые были приняты для старых систем фирмы Microsoft и совместимых с ними. Это объектные файлы, которые могут быть собраны в исполняемые файлы компоновщиками link.exe, tlink.exe и tlink32.exe, соответственно фирм Microsoft и Borland, а также ряд компоновщиков от других разработчиков (в частности компоновщик alink.exe, входящий в комплект системы программирования NASMW). Формат win32, называемый более точно OMF (Object Microsoft Format), генерируется современными компиляторами фирмы Microsoft (masm.exe, cl.exe средой MS Visual C++).

21

Наконец, формат elf - это стандартная форма объектных файлов, производимых компиляторами в Linux, именно он и потребуется нам в первую очередь.

Вызов транслятора nasm для Linux в простеющем случае имеет вид nasm -f elf имя_ассемберного_файла

Имя файла результата, формируемое транслятором из файла исходного текста, зависит от используемого формата, если только это результирующее имя не указывается явно. Так объектные файлы для операционных систем Windows получают расширение OBJ - при той же основной части имени, что и у исходного файла. Объектные файлы для Unix получают автоматически расширение .o (используемое для объектных файлов в этих системах). Явное указание оказывается нужным при использовании формата bin, поскольку автоматически создается файл с той же основной частью имени, но без расширения. Для получения исполняемых файлов ОС MS DOS, являющимися наиболее простыми для этой системы - так называемые односегментными, требуется расширение .COM в имени этих файлов. Для достижения этой цели помогает вспомогательная опция явного указания имени результирующего файла. Она записывается в виде

-o имя_результирующего_файла

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

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

-l имя_файла_листинга

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

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

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

ld -o имя_исполныемого_файла имя_объектного_файла

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

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

и назвав его, например, nasml. Тогда построение исполняемого файла из исходного файла с именем prog1.asm может быть получено вызовом в командной строке

./nasml prog1

(Естественно, предполагается, что командный файл nasml находится в текущем каталоге; если же он находится в каталоге, по которому ведется автоматический поиск согласно переменной окружения PATH, то достаточно вызвать его в виде nasml

22

prog1.) В ходе выполнения командного файла будет построен промежуточный файл с именем prog1.o - как результирующий при работе программы nasm - и файл листинга с именем prog1.lst, из которых программ компоновщика должна построить исполняемый файл с именем prog1.exe. (В действительности какой-то или какие-то файлы из перечисленных могут не быть созданы, если в ходе выполнения программ, вызываемых в командном файле, будут обнаружены ошибки, мешающие такому созданию.)

Разработка простейших программ, предназначенных выполнятся в операционной системе MS-DOS может быть выполнена под любой из ОС типа MS Windows.

Вызов транслятора NASMW.EXE, выполняемого в любой ОС типа Windows, для простейщих ассемблерных программ целесообразно выполнять в виде

nasmw -f bin -o имя_файла.com имя_файла.asm -l имя_файла_листинга

или же путем использования командного файла, например с именем stnasm.bat (при использовании ОС Windows NT файла с именем stnasm.cmd), который содержит единственную строку

nasmw -f bin -o %1.com -l %1.lst %1.asm путем вызова в виде

stnasm имя_без_расширения_файла_программы

Эти варианты порождают непосредственно исполняемый файл с расширением com, без построения промежуточных объектных файлов (но только в случает отсутствия ошибок).

Сам текст программ, предназначенный для MS-DOS, в этом случае должен использовать соглашения по доступу к системным функциям, принятые именно в этой ОС. Эти соглашения гораздо менее систематичные, чем в других ОС.

Основная особенность для вызова системных функций в MS-DOS заключается в использовании только 16-битных и 8-битных регистров. (Когда создавалась эта ОС, процессоры компьютеров IBM PC еще не имели 32-битной архитектуры!) Большинство системных функций MS-DOS используют для доступа к ядру OC номер прерывания 21h, так что само такое обращение записывается командой INT 21h. Номер функции при этом задается в регистре AH, остальные регистры хотя и служат для передачи аргументов, но не использую при этом общего правила.

Вызов системной функции write требует номера функции 40h (напомним, что в регистре AH), при этом хэндл, в частности хэндл стандартного вывода, должен быть помещен в регистр BX, адрес (который, на самом деле является относительным) - в регистр DX, а число выводимых байтов - в регистр CX. Поэтому фрагмент программы, выводящий текст из именованной области txt и имеющий 7 символов, запишется в виде

;--- write(1, txt, 7) == <40h>(bx, dx, cx) mov ah,40h ; N function=write

mov bx,1 ; N handle=1 (hstdout) mov cx,7 ; number of byte

mov dx, txt ; address of text

23

int 21h

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

Сама программа для односегментных программ MS-DOS имеет следующие характерные особенности. Во-первых, для нее допускается описание только одного сегмента (это уникальная ситуация, присущая только этому виду исполняемых программ и только в указанной ОС), причем этот сегмент при использовании формата bin результирующего файла в вызове транслятора NASM должен иметь стандартное имя .text (и никакое другое, иначе диагностируется ошибка). Во-вторых, в самом начале сегмента следует разместить служебную директиву org 100h (или org 256, что то же самое). Тем самым резервируется служебная область для системной таблицы в начале исполняемых файлов типа COM и унаследованная от непосредственного предшественника ОС MS-DOS. Сразу после директивы org должны следовать команды, а область данных целесообразно размещать в конце единственного сегмента. Разместить данные можно и внутри области команд, в частности, после первой команды программы, но при этом перед началом данных необходимо использовать команду JMP метка, действия которой будут рассматриваться позже и которая предназначена для "обхода" области данных при выполнении программы.

Еще одной особенностью является возможность в качестве приказа завершения, аналогичного вызову exit(0) из языка СИ, использовать единственную команду

NT 20H

Текст односегментной программы для выполнения в MS-DOS приведен на рис. 2.3.1.

SEGMENT .text org 100h

start:

;--- write(1, txt, 7) == <40h>(bx, dx, cx) mov ah,40h ; N function=write

mov bx,1 ; N handle=1 (stdout) mov cx,7 ; number of byte mov dx, txt ; address of text

int 21h

;--- write(1, symbl, 1) == <40h>(bx, dx, cx) mov ah,40h ; N function=write

mov bx,1 ; N handle=1 (stdout) mov cx,1 ; number of byte mov dx, symbl ; address of symb

24

int 21h

int 20h ; function=exit for COM-file ;;--- SEGMENT .data

txt db 'Privet!' symbl db '$'

Рис. 2.3.1. Простейшая односегментная программа для MS-DOS

Для удобства программиста в трансляторе NASM имеются дополнительные неявные средства. При формате результата bin они предоставляют возможность помещения данных в отдельный сегмент данных, который транслятор при построении результирующего файла помещает в конец кодов исполняемой программы. Не следует увлекаться этой возможностью, так как другой путь построения односегментной программы через объектные файлы с форматом obj не приемлют такой вариант. Тем ни менее для демонстрации возможностей он приведен на рис. 2.3.2.

; Область данных автоматически помещается в конце кодов программы при формате bin

org 100h

SEGMENT .data ;; must to be ! txt db 'Privet!'

symbl db '$' SEGMENT .text

start:

mov ah,40h ; N function=write mov bx,1 ; N handle=1 (stdout) mov cx,7 ; number of byte mov dx, txt ; address of text

int 21h

mov ah,40h ; N function=write mov bx,1 ; N handle=1 (stdout) mov cx,1 ; number of byte mov dx, symbl ; address of symb int 21h

int 20h ; function=exit for COM-file

Рис. 2.3.2. Программа для MS-DOS с неявно включаемым сегментом данных

2.4. Управление строками при выводе и ввод данных

При выполнении программ, приведенных исходными текстами на рис. 2.2.1, 2.3.1 и 2.3.2, текст из внутренней области txt и далее выводимый символ из внутренней области symbl сливаются в общий текст. Более того, в Linux последующий

25

ввод приглашения для дальнейшей работы также сливается с введенным текстом без перехода на новую строку. Даже в таких простейших примерах происходящее не очень удобно. Поэтому в любой из операционных систем, ориентированных на диалоговое взаимодействие с пользователем, присутствуют простые средства управления выводом на экран. Этими средствами являются управляющие символы, которые на языке Си изображаются внутри текста специальными символьными комбинациями '\n' и '\r'. В рассматриваемых ассемблерах такие символьные комбинации не используются (но используются в специализированном ассемблере AT&T, вызываемом командой as ОС Unix). Вместо управляющих символьных комбинаций следует использовать числовые значения управляющих символов. Ими являются, соответственно, числа 10 и 13. Причем для перевода на новую строку с автоматическим переходом в ее начала для Unix достаточно использовать единственный управляющий символ со значением 10, а в MS-DOS указанные управляющие символы действую независимо друг от друга.

Для ввода данных из файлов в Unix и MS-DOS предназначена системная функция, задаваемая следующим прототипом на языке Си:

unsigned int read(int handle, char *buffer, unsigned int size).

Число действительно введенных байтов эта функция возвращает в качестве собственного значения. Это число включает не только явно видимые символы, но и завершающий символ перевода строки '\n', образуемый при вводе с клавиатуры нажатием клавиши Enter. Например, число, возвращаемое функцией read после ввода с клавиатуры текста Privet, будет равно 7 (а вовсе не шести, как можно было бы подумать).

При реализации функций, возвращающих значения, на системном уровне принято, что такие значения при возможности возвращаются в регистре eax или, если используемая ОС 16-битная, то в регистре ax. Естественно, что это соглашение используется и для собственно системных функций.

Последняя информация дает нам возможность составить программу, которая использует ввод данных с клавиатуры. Для этого достаточно использовать хэндл стандартного ввода, имеющий в Unix и MS-DOS константное значение, равное нулю. На рис. 2.4.1 приведена программа для Linux, которая

GLOBAL _start SEGMENT .text

_start:

;--- read(0, buf, 20) == <3>(ebx, ecx, edx) mov eax,3 ; N function=write

mov ebx,0 ; N handle=1 (stdout) mov ecx, buf ; address of txt mov edx,20 ; number of byte int 80h

add eax, 9 ; ; sum length of txt + actual read

26

mov edx, eax

;--- write(1, txt, len) == <4>(ebx, ecx, edx)

 

mov eax,4 ; N function=write

 

mov ebx,1 ; N handle=1 (stdout)

 

mov ecx, txt ; address of txt

 

int 80h

 

mov eax,1 ; N function=exit

 

int 80h

 

SEGMENT .data

txt

db 'Vvedeno: '

buf

times 20 db 0

 

Рис. 2.4.1. Использование элементарных ввода и вывода в Linux

после запуска ожидает ввода текста (не более чем двадцати символов) и после этого отображает введенный текст с предшествующим ему текстовым примечанием, взятым из области данных с именем txt. Полученное в регистре eax число введенных символов складывается с известным числом символов в области txt, а результат переносится в регистр edx, который в ближайшем обращении к функции вывода должен содержат число выводимых символов. Заметим, что в число символов, выводимых программой, входит и управляющий символ перевода строки, помещенный в буфер buf системной функцией read и учитываемый возвращаемым значением в регистре eax.

Само сложение выполняется с помощью команды ADD, которая по своему назначению складывает число, заданное вторым операндом, со значением, заданном в первом операнде. Практически действие этой команды аналогично действиям оператора += из языка Си и результат сложения размещается в месте, указанным первым операндом.

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

На рис. 2.4.2 приведена программа, которая делает то же самое, но предназначена для MS-DOS.

SEGMENT .text org 100h

start:

;--- read(0, buf, 20) == <3fh>(bx, dx, cx) mov ah,3fh ; N function=write

mov bx,0 ; N handle=1 (stdout)

27

mov cx,20 ; number of byte mov dx, buf ; address of text int 21h

add ax, 9 ; sum length of txt + actual read mov cx, ax

;--- write(1, txt, len) == <40h>(bx, dx, cx) mov ah,40h ; N function=write

mov bx,1 ; N handle=1 (stdout) mov dx, txt ; address of text

int 21h

int 20h ; function=exit for COM-file

;;---

SEGMENT .data

txt

db 'Vvedeno: '

buf

TIMES 20 db 0

 

Рис. 2.4.2. Использование элементарных ввода и вывода в MS-DOS

2.5. Простейшие способы адресации

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

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

28

MOV EDX, EAX

оба ее операнда заданы регистровым способом адресации, а в команде MOV CL, 654

левый операнда задан регистровым способом адресации, а правый другим способом.

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

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

x

dd

56

y

dd

-37

z

dd

0

тогда команды MOV eax, [x] ADD eax, [y] MOV [z], eax

обеспечивают сложения значений из 4-х байтовых полей данных с именами x и y с последующим помещением результата в двойное слово области z. Практически эти команды реализуют оператор Си

z = x + y

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

MOV eax, [00000020] ADD eax, [00000024] MOV [00000028], eax

отражающие, в частности, тот факт, что именованные в исходном файле области x, y, z в сегменте данных исполняемого файла будут находится со смещениями 20, 24 и 28 от начала этого сегмента. Именно поэтому для обозначения на ассемблере дан-

29

ных в памяти компьютера используются вспомогательные синтаксические элементы - квадратные скобки. Убрав из последнего фрагмента команд, мы бы получили, что первая команда приказывает занести числовую константу 20 в регистр eax, а вторая команда приказывает прибавить к содержимому регистра eax значение числовой константы 24. Последние приказания совершенно отличны от задаваемых в исходном файле действий, где область данных наименованная x содержит число 56, а область данных, наименованная y, содержит число -37. Другое дело, что область, ранее обозначенная x, в машинном коде обозначается просто как лежащее со смещением 20 от начала сегмента данных, а область, ранее обозначенная как y, лежит со смещением 24 от начала сегмента данных. (Заметим, что в языках высокого уровня никогда не появляются действительные обозначения переменных числовыми смещениями от начала сегмента и, поэтому, нет никакой необходимости вводить специальные символы, подчеркивающие, что имя обозначает место в памяти,

ане само значение, как бывает для констант, размещаемых внутри кода команды.)

Сточки поверхностного программирования действие команды

MOV edx, 127 равносильно действию команды

MOV edx, [kkk]

где имя kkk определено в сегменте данных как kkk dd 127

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

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

Для частичной демонстрации рассмотренных способов адресации обратимся к более содержательному примеру, представленному программой на рис. 2.5.1. Эта программа вводит любой текст, набираемый пользователем (но не более 80 символов) и затем заменяет в нем внутри программы второй из введенных символов на символ ! (восклицательный знак). Преобразованный таким образом текст выводится на экран.

;Ввод строки текста, изменение в ней второго из введенных на символ ! и

;вывод полученной строки

30

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