- •Аппаратно-ориентированное программирование
- •Ббк 32.973.73
- •Удк 681.3 ббк 32.973.73ф 73
- •1. Основы программирования на ассемблере
- •1.1. Принципы построения ассемблерных программ
- •1.2. Понятие архитектуры компьютера
- •1.3. Регистры программиста в ia32
- •1.4. Описание сегментной структуры программы
- •2. Простейшие средства ассемблера
- •2.1. Средства описания данных
- •2.2. Обращения к функциям ос посредством прерываний
- •2.3. Средства преобразования в исполняемый файл
- •2.4. Управление строками при выводе и ввод данных
- •2.5. Простейшие способы адресации
- •3. Архитектурные элементы для построения программ
- •3.1. Организация условных переходов
- •3.2. Средства организации циклов
- •3.3. Особенности команд умножения и деления
- •3.4. Организация процедур
- •3.5. Неарифметические операции над кодами
- •4. Использование неэлементарных способов адресации
- •4.1. Косвенно-регистровая адресация
- •4.2. Использование индексной адресации данных
- •4.3. Базовая и индексно базовая адресации
- •4.4. Адресация с масштабированием
- •5. Взаимосвязи программных единиц
- •5.1. Многомодульная разработка программ
- •5.2. Использование библиотек объектных модулей
- •5.3. Организация стекового кадра подпрограммы
- •5.4. Программный доступ к системным функциям Win32
- •5.5. Особенности использования объектных файлов формата coff
- •5.6. Стандартный доступ к системным функциям Unix
- •6. Вспомогательные средства базовой архитектуры
- •6.1. Использование строковых команд пересылки
- •6.2. Применение строковых команд сравнения
- •7. Использование ассемблерных отладчиков
- •7.1. Особенности отладчика gdb для программ в Linux
- •7.2. Отладчики текстового режима для Windows
- •Библиографический список
- •Оглавление
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.)
Кроме того, при компоновке необходимо указать использование самой библиотеки 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.
GLOBAL _start
EXTERN write, exit, printf
SEGMENT .text
_start:
;--- write(1, txt, lentxt) == <4>(ebx, ecx, edx)
push dword lentxt
push dword txt
push dword 1
call write
add esp, 12
; call printf(formt, x, y)
push dword [y]
push dword [x]
push dword formt
call printf
add esp,12
push dword 1
call exit
SEGMENT .data
txt db 'Privet!',27,'[10;40H',27,'[1;31;44mOu-key!'
db 27,'[0m',10
lentxt equ $-txt
x dd 3456
y dd -78881
formt db 'X=%d, Y=%d',10,0
Листинг 5.6.3. Пример использования системных функций через библиотеку libc
В этой программе использованы системные функции write и exit ОС Linux, а также очень удобная функция форматированного вывода printf, хорошо известная всем программистам на языке Си.
Еще раз подчеркнем, что для компоновки с помощью gcc, завершающей построение исполняемого файла (вместо компоновки с помощью комповщика ld), в этой программе строки
GLOBAL _start
и
_start:
должны быть заменены, соответственно, на строки
GLOBAL main
и
main:
С помощью стандартного вывода в рассматриваемой программе на экран выводятся фрагменты текста «Privet!» и «Ou_key!», которые сопровождаются управляющими последовательностями в самом тексте, который представляется на вывод функцией write. Управляющие последовательности позволяют позиционировать курсор (переустанавливать его в любое место экрана и тем самым направлять вывод в любое место экрана), а также управлять цветовыми атрибутами вывода. Подробней о управляющих последовательностях смотрите соответствующую литературу, например [15]. (Заметим, что управляющие последовательности доступны, конечно, не только через стандартные библиотеки, но и выводом через прерывание INT 80h, используемое в Linux на более низком уровне.)
Для использования функции printf в ассемблерной программе ее первый аргумент описан отдельно текстом в сегменте данных и обозначен именем formt. Этот текст включает служебные символы формата %d, которые обеспечивают задание преобразования значений, заданных последующими аргументами в обозримый для человека формат десятичных чисел.
Упражнения.
1. Построить библиотеку объектных модулей Linux, содержащих доступные извне процедуры с именами atoi, itoa. Процедура atoi должна выполнять преобразование последовательности цифр, заданной адресом, который содержит регистр eax, в двоичный код, выдаваемой ее также через регистр eax. Процедура itoa должна выводить в стандартный вывод последовательность десятичных цифр, представляющих исходное значение аргумента в регистре eax, задаваемое им в двоичном коде. Кроме того, библиотека должна содержать процедуру lfwrite, которая переводит вывод на экране на следующую строку и рассмотренные выше процедуры stdread, stdwrite, stdexit, собранные в один объектный файл. Дополнительно построить программу, которая использует все эти процедуры и при разработке которой используется данная библиотека объектных модулей.
2. Построить библиотеку объектных модулей, описанных в первом упражнении, для ОС Windows и разработать тестовый пример проверки функционирования этих модулей.
3. Построить ассемблерную программу, использующую процедуру вычисления функции Аккермана. Эта функция задается рекуррентными соотношениями
A(0,M)=M+1; A(N,0)=A(N-1,1); A(N,M)=A(N-1, A(N,M-1))
и естественный способ ее вычисления заключается в рекурсивных вызовах вычисляющей процедуры. Главная часть программы должна вводить числовые значения аргументов M и N, а затем обращаться к процедуре вычисления функции. Результат выводится на экран. При отладке начинать проверку работы с очень небольших значений, в частности полагая N<3, иначе есть риск не дождаться результата за приемлемое время.