Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Флоренсов А.Н. УП Системное программное обеспечение.docx
Скачиваний:
46
Добавлен:
28.06.2021
Размер:
148.95 Кб
Скачать

5.5. Вызов функций из стандартных библиотек Linux

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

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

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

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

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

Он требует явного указания в составе команды вызова программы ld опции

–dynamic–linker полное_имя_динамического_компоновщика

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

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

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

nasm –f elf64 $1.asm –l $1.lst

ld –o "$1".exe "$1".o –dynamic-linker /lib64/ld-linux-x86-64.so.2 –lc

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

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

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

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

nasm –f elf32 $1.asm –l $1.lst

gcc –m32 –o "$1".exe "$1".o

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

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

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

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

В качестве примера вызовов системных функций с помощью стандартной библиотеки рассмотрим программу, приведенную в листинге 5.5.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.5.3. Пример использования системных функций через библиотеку libc

В этой программе использованы системные функции write и exit ОС Linux, а также очень удобная функция форматированного вывода printf, хорошо известная всем программистам на языке Си.

Еще раз подчеркнем, что для компоновки с помощью gcc, завершающей построение исполняемого файла (вместо компоновки с помощью комповщика ld), в этой программе строки

GLOBAL _start

и

_start:

должны быть заменены, соответственно, на строки

GLOBALmain

и

main:

С помощью стандартного вывода в рассматриваемой программе на экран выводятся фрагменты текста 'Privet!' и 'Ou_key!', которые сопровождаются управляющими последовательностями в самом тексте t xt. Управляющие последовательности позволяют позиционировать курсор (переустанавливать его в любое место экрана и тем самым направлять вывод в любое место экрана), а также управлять цветовыми атрибутами вывода. Подробней об управляющих последовательностях можно узнать из соответствующей литературы. (Заметим, что управляющие последовательности доступны, конечно, не только через стандартные библиотеки, но и выводом через прерывание INT 80h, используемое в Linux на более низком уровне.)

Для использования функции printf в ассемблерной программе ее первый аргумент описан отдельно текстом в сегменте данных и обозначен именем formt. Этот текст включает служебные символы формата %d, которые обеспечивают задание преобразования значений, указанных последующими аргументами в обозримый для человека формат десятичных чисел.

Для 64-битных Linux, реализованных в архитектуре x86_64, обращение к функциям системной библиотеки языка Си требует обычного вызова функций через команду CALL, но аргументы в простейших случаях (целочисленных аргументах и числе аргументов не более шести) помещаются в 64-битные регистры. Причем используются правила ABI от разработчика архитектуры (AMD64), согласно которым 1-й аргумент должен размещаться в регистре EDI, второй – в регистре ESI, третий – в регистре EDX, 4-й – в регистре ECX, 5-й – в регистре R8, а шестой – в регистре R9. Последующие аргументы, если они есть (а согласно идеологии и традициям Unix программирования, в системных разработках следует стремиться к созданию функций с возможно меньшим числом аргументов), последовательно размещаются в стеке согласно порядку по соглашению языка С.

Следующий пример демонстрирует использование указанных соглашений (листинг 5.5.4).

EXTERN write, exit, printf

SEGMENT .text

GLOBAL _start

_start: mov rdx, len

mov rsi,txt

mov rdi, 1

call write ; не надо add esp, 12

mov rdx, lena

mov rsi, txta

mov rdi, 1

call write ; не надо add esp, 12

mov rax, 0

mov rdx, [y]

mov rsi, [x]

mov rdi, format

call printf ; не надо add esp, 12

mov edi,0

call exit

SEGMENT .data

txt db "Privet from 2014 Linux64libc", 10

len equ $-txt

txta db "Ura from Linux64 else", 10

lena equ $-txta

x dq 145

y dq –3389

format db 27,'[40;10H',27,'[1;31;40mElse privetic! %d %d',27,'[0m',10,0

;; nasm –felf64 prog.asm

;; ld –dynamic-linker /lib64/ld-linux-x86-64.so.2 prog.o –lc

Листинг 5.5.4. Пример 64-битных вызовов функций через библиотеку libc