Материал: TextmetukSPO

Внимание! Если размещение файла нарушает Ваши авторские права, то обязательно сообщите нам

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

Служебная процедура инициализации и завершения может отсутствовать в программе на языке Си для операционных систем OS/2 и Windows. Тогда эта отсутствующая процедура подменяется компоновщиком для этих систем на стандартную, которая в дальнейшем вызывается, но "ничего не делает", сообщая только возвращаемым значением, что инициализация библиотеки прошла нормально. Написание же содержимого DLL исключительно на ассемблере неизбежно влечет необходимость включения явных служебных процедур.

Для изучения содержимого разделяемых библиотек в Windows служат многофункциональные утилиты DUMPBIN.EXE из комплекта средств разработки Microsoft и TDUMP.EXE из комплекта разработки Borland/Inprise.

В частности запрос вывода экспортируемых имен на экран с помощью DUMPBIN будет иметь вид

DUMPBIN -export имябиблиотеки

а вывод импортируемых имен из исполняемого файла достигается вызовом DUMPBIN -import имяEXEфайла

Служебная утилита TDUMP единообразно решает обе задачи и, в простейшем случае, вызывается с единственным аргументом - именем исполняемого файла, в качестве которого может использоваться как разделяемая библиотека, так и обычный исполняемый файл.

Задание. Построить DLL библиотеку Windows, функции которой позволяют запомнить внутри нее текст данных, а затем - в другой функции - прочитать этот текст в вызывающую программу. Использовать эту библиотеку в двух параллельно работающий процессах, где основная программа использует динамическую компоновку времени загрузки, один из программных модулей – либо DLL библиотека, либо основная программа - написан на языке ассемблера, а другой из этих модулей – на языке Си; библиотека DLL обеспечивает выдачу сообщений о подключении к ней процесса и об отключении его от нее.

Контрольные вопросы.

1.Чем использование библиотеки динамической компоновки отличается от использование библиотеки объектных модулей.

2.Сколько сегментов машинных команд использует DLL, разработанная вами

влабораторной работе, во время выполнения основной программы этой работы?

3.Сколько сегментов данных использует DLL, разработанная вами в лабораторной работе, во время выполнения основной программы этой работы?

4.Выскажите свое мнение по возможности и целесообразности применения того или иного из изученных методов подключения DLL при программировании на языке С++.

5.Чем косвенные вызовы подпрограмм на ассемблере отличаются от прямых вызовов подпрограмм, кроме самого синтаксиса записи в программе такого вызова?

Лабораторная работа №11

46

Содержание работы. Изучение средств и методов использования динамической компоновки в операционных системах тип Linux.

Предварительные сведения.

В операционной системе Linux ассемблерные программы для компилятора NASM, предназначенные для вызова извне разделяемой библиотеки? требуют дополнительных указаний. Они заключаются в дополнении имени подпрограмм служебным модификатором, который задается служебным словом function и отделяется от имени подпрограммы символом двоеточия. Это уточнение относиться только к указанию имени подпрограммы в качестве операнда директивы GLOBAL.

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

void wiwoda(char *pt)

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

 

GLOBAL wiwoda:function

 

EXTERN printf

 

SEGMENT .data

txt

db 'We are into Dll .. ',10

lentxt equ $-txt

frmt

db 'Text argument is <%s>',10,0

wiwoda:

;;аналог функции wiwoda(char *pt)

push ebp

mov ebp,esp

pusha

 

mov eax,4

mov ebx,1

mov ecx, txt ; для вызова функции printf(frmt, pt) mov edx, lentxt

int 80h

push dword frmt call printf

add esp, 8 popa

pop ebp ret

Листинг 9. Программа updll.asm на ассемблере NASM

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

nasm -f elf -o updll.exe updll.asm ld -shared -o myupdll.so updll.o

47

Для простейшего использования разделяемой библиотеки myupdll.so можно использовать программу, написанную на языке Си, которая приведена в листинге 10.

#include <stdio.h>

extern wiwoda(char *pt, int n); int main()

{int k;

printf("Before call my Dll\n"); k=wiwoda("Text for our DLL", 10*n); printf("After call -- Dll, k=%d\n", k); k=wiwoda("Second text for our DLL", 20); printf("After call -- Dll, k=%d\n",k); return 0;

}

Листинг 10. Программа exdll.c для использования разделяемой библиотеки

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

gcc -o exdll.exe exdll.c `pwd`/myupdll.so

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

Документация на gcc рекомендует использовать предварительный вызов компилятора с опцией -fPIC. В частности программу updll2.c следует обработать командным вывовом

gcc -c -fPIC updll2.c

Результатом работы этой компиляции будет объектный файл с именем updll2.o. В данной командной строке вызывается GNU компилятор с языка Си. Он использует две опции. Первая из них (опция -с) указывает, что будет выполняться только компиляция без последующей компоновки в данном командном вызове. Вторая опция (-fPIC) заставляет компилятор генерировать командный код, выполнение которого не зависит от положения фрагмента программы (реализация этой опции зависит от возможностей конкретной архитектуры). Далее следует компоновка вызовом командной строки

ld -shared -o mydll2.so.1.0 updll2.o

Команда является именем стандартного компоновщика в Unix. Первая опция этого вызова задают разделяемый объект (опция -shared). Опция -o имя задает имя результирующего файла, в данном случае создается файл с именем mydll2.so.1.0. Последний элемент командной строки определяет исходный файл, обрабатываемый компоновщиком.

Вместо отдельных вызовом компилятора и компоновщика для создания библиотеки она может быть построена вызовом одной командной строки

gcc -shared -o mydll2.so.1.0 updll2.c

Следует обратить внимание, что в Unix принята очень свободная система обозначения файлов, в частности собственное имя файла может включать не

48

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

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

libимя.so.гл.мл.вер

где имя - собственная часть имени, гл - главный номер версии, мл - младший номер версии, вер - вариант (patch) версии. Например, такая библиотека может иметь название libc.so.4.3.2, где главный номер версии равен 4, младший номер версии равен 3, а вариант версии равен 2. Принято очень удобное для использование соглашение, по которому изменение младшего номера (его увеличение) соответствует только добавлению новых функций в библиотеку без изменения ранее туда помещенных, так что сохраняется обратная совместимость (библиотека без каких-либо изменений поддерживает старые функции в ней). Увеличение варианта соответствует только устранению обнаруженных ошибок. Более серьезные изменения приводят к увеличению главного номера библиотеки, при котором не гарантируется точная поддержка более ранних функций.

В связи с этой системой собственное имя DLL библиотеки как правило никогда непосредственно не используется для ссылок на нее в исполняемых файлах. В качестве промежуточного звена используются обобщенное имя без номера варианта и обобщенное имя, не включающее ни один из внутренних номеров версии. Таким образом для приведенного примера libc.so.4.3.2, программы, использующие эту библиотеку будут ссылаться внутри себя только на имя libc.so, а в файловой системе должен быть файл с этим обобщенным именем, ссылающимся на действительное имя библиотеки. Такие имена файлов, представляющие собой всего лишь ссылки на действительные файлы или другие имена называются символическими ссылками в файловой системе и создаются в Unix командой ln с опцией -s. Таким образом для использования DLL библиотеки с собственным именем myd.so.1.0 следует создать символическую ссылку с именем myd.so.1, указывающей на это собственное имя и ссылку с именем myd.so, указывающую на имя myd.so.1. Создание этих ссылок выполняется командами

ln -s myd.so.1.0 myd.so.1 ln -s myd.so.1 myd.so

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

gcc uxd2.с путь/myd.so

где путь задает точное место размещения файла обобщенной ссылки на библиотеку myd в дереве файловой системы Unix. Например, если библиотека вместе со ссылками построена в каталоге /home/sdudent, то рассматриваемая команда должна быть выдана в виде

gcc uxd2.с /home/sdudent/myd.so

49

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

Старый стиль построения трансляторов для языка Си в Unix и трансляторов для 16-битной архитектуры персональных компьютеров систематически использовал следующее правило определения действительных внешних имен. Имена, записанные на языке Си, дополнялись при занесении в объектные файлы дополнительным символом подчеркивания в качестве префикса имени. Например, имя подпрограммы, записываемое на языке Си как printf, в объектном файле записывалось как _printf. При использовании ассемблерных вставок в программе Си имена локальных переменных могли быть в любой момент обозначены также с помощью дополнительного предшествующего символа подчеркивания. Так локальные переменные i и j в ассемблерной вставке должны были обозначаться как _i и _j.

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

При динамической компоновке времени выполнения в операционной системе Linix для запроса установления связи DLL библиотекой служит функция

void* dlopen(char * namedll, int mode);

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

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

Для получения адреса процедуры в этой операционной системе предназначена функция

void* dlsym(int hdll, char *nameproc),

где hdll -хэндл библиотеки, полученный из функции dlopen, nameproc - адрес имени процедуры, заданного символьным текстом. Функция эта возвращает в качестве своего значения адрес требуемой процедуры или 0 - в случае неудачи.

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

50

Источник: https://studfile.net/preview/16469866/