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

Textnew2

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

 

GLOBAL wiwoda:function

 

EXTERN printf

 

SEGMENT .data

txt

db 'We are into Dll .. ',10

lentxt equ $-txt

frmt

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

wiwoda:

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

push ebp

mov ebp,esp

pusha

 

mov eax,4 mov ebx,1 mov ecx, txt mov edx, lentxt int 80h

; для вызова функции printf(frmt, pt) push dword [ebp+12]

push dword [ebp+8] push dword frmt call printf

add esp, 12 popa

mov eax, [ebp+12] lea eax, [2*eax] pop ebp

ret

Рис. 8.2.5. Программа updll.asm на ассемблере NASM

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

nasm -f elf updll.asm

ld -shared -o myupdll.so updll.o

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

#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", 37);

191

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;

}

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

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

gcc -o exdll.exe exdll.c путь/myupdll.so

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

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

#include <stdio.h>

int wiwoda(char* text, int n)

{ printf("New Into DLL..ist...\n"); printf("%s\n", text);

return 2*n;

}

Рис. 8.2.7. Программа updll2.c для Linux.

следует обработать командным вывовом gcc -c -fPIC updll2.c

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

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

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

192

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

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

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

С другой стороны для обозначения библиотек динамической компоновки в Linux принята четко сложившаяся система. Системные 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

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

193

#include <stdio.h>

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

{int k;

printf("Before DLL\n"); k=wiwoda("Text For DLL mmmm", 10); printf("After DLL mmm k=%d\n",k); return 0;

}

Рис. 8.2.8. Программа uxd2.c для Linux.

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

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

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

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

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

8.3. Использование объектных модулей формата COFF

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

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

194

использует новый формат объектных файлов, называемый ею COFF (Common Object File Format), но в ассемблере NASM обозначаемый win32. Для этого формата использована не бросающаяся в глаза особенность, заключающаяся в том, что хотя имена в нем обязательно дополнены префиксными символами подчеркивания, но в директивах компоновки имена указываются с опущенным символом подчеркивания. Практически это значит, что все внешние имена для данного формата, предназначенные для использования в библиотеках динамической компоновки, должны в объектном файле иметь символы подчеркивания (иначе компоновщик не сможет их использовать).

Более того, все действительные имена системных функций ОС Windows должны строиться из имен прототипов на языке Си путем дополнения не только предшествующим символом подчеркивания, но и суффиксом вида @число_байтов_в_области_аргументов. В частности, из имени GetStdHandle при этом получается имя _GetStdHandle@4, а из имени WriteFile получается имя _WriteFile@20. Именно такие "настроенные" имена можно увидеть при просмотре объектного файла, полученного 32-битным транслятором MS из программы Си, которая использует указанные функции GetStdHandle и WriteFile.

В качестве примера рассмотрим программы wp1.asm и wp2.asm, первая из которых предназначена для использования библиотеки динамической компоновки, построенной из второй программы. Эти программы приведены на рис. 8.3.1 и 8.3.2.

extern _ExitProcess@4 SEGMENT .data

tttdb 'Text for dll---',0 SEGMENT .text

EXTERN _wiwoda ; ассемблерный аналог функции wiwoda(char *t) GLOBAL _start

_start:

push dword 37 push dword ttt call _wiwoda add esp,8

push dword 1

call _ExitProcess@4

Рис. 8.3.1. Программа wp1 вызова функции _wiwoda из DLL-библиотеки

STD_OUTPUT_HANDLE EQU -11 INVALID_HANDLE_VALUE EQU -1

extern _ExitProcess@4, _GetStdHandle@4 extern _FillConsoleOutputAttribute@20 extern _WriteConsoleOutputCharacterA@20 SEGMENT .data

195

text

db 'Мы находимся внутри DLL!!! var 1'

lenequ $-text

lena

dd 0

cbActual dd 0 hstdout dd 0 pos:

col dw 0 row dw 0 attr dd 1eh

SEGMENT .text GLOBAL _wiwoda _wiwoda:

push ebp mov ebp, esp pusha

push dword STD_OUTPUT_HANDLE call _GetStdHandle@4

mov [hstdout], eax

cmp eax, -1; значение INVALID_HANDLE_VALUE je near kon

mov word [col],20 mov word [row], 9 push dword cbActual push dword [pos] push dword len push dword [attr] push dword [hstdout]

call _FillConsoleOutputAttribute@20 push dword cbActual

push dword [pos] push dword len push dword text push dword [hstdout]

call _WriteConsoleOutputCharacterA@20 mov esi, [ebp+8]

xor edx, edx mov ecx, 80

cmm: cmp byte [esi],0 je au

inc esi inc edx

loop cmm ; вычисление числа символов в текстовом аргументе функции

196

au:inc word [row] mov [lena], edx

push dword cbActual push dword [pos] push dword [lena] push dword [attr] push dword [hstdout]

call _FillConsoleOutputAttribute@20 push dword cbActual

push dword [pos] push dword [lena] push dword [ebp+8] push dword [hstdout]

call _WriteConsoleOutputCharacterA@20 mov eax, [ebp+12]

imul eax, 20 exi: popa

pop ebp ret

kon: push dword 1

call _ExitProcess@4

GLOBAL __DllMainCRTStartup@12 __DllMainCRTStartup@12: mov eax,1

ret 12

Рис. 8.3.2. Программы wp2 с функцией _wiwoda для DLL-библиотеки

Кроме необходимости использовать в ассемблерных программах для DLL оформленных указанным способом имен, в программе для DLL требуется учесть еще одну особенность. Как уже упоминалось в разделе 8.2, каждая DLL должна содержать специальную процедуру инициализации. По умолчанию компоновщик link.exe фирмы Microsoft принимает для этой процедуры действительное имя __DllMainCRTStartup@12. При желании, можно имя такой инициализирующей процедуры переопределить, используя опцию компоновщика /ENTRY. Она записывается при вызове LINK.EXE в виде /ENTRY:инитимя, но действительное имя точки входа в такую процедуру должно быть написано на ассемблере в виде _ инитимя@12 (т.е. соответствовать описанным выше соглашениям по использованию имен системных функций для формата COFF объектных файлов Microsoft). Подробней с функциями инициализации для DLL в общем случае мы будет разбираться позже. Пока нам достаточно, что эта функция должна быть описана как получающая 12 байтов аргументов и возвращать - для нормальной работы - отличное от нуля значение.

Построение исполняемых файлов из программ wp1.asm и wp2.asm может быть задано командным файлом (или простой последовательность команд оболочки ОС)

197

nasmw -f win32 wp1.asm nasmw -f win32 wp2.asm

link /DLL /export:wiwoda wp2.obj kernel32.lib

link /subsystem:console /entry:start wp1.obj kernel32.lib wp2.lib

Рис. 8.3.3. Командный файл построения и использования DLL

Здесь вызов компоновщика с опцией DLL формирует библиотеку с неявно заданным именем wp2.DLL (собственное имя которой совпадает с именем первого или единственного объектного файла, обрабатываемого компоновщиком link). Кроме того, и это отличительная особенность рассматриваемого компоновщика фирмы MS, одновременно с библиотекой DLL автоматически формируется библиотека импорта, имеющая то же собственное имя, но в качестве расширения буквосочетание LIB. Последняя библиотека используется в последующем вызове компоновщика для формирования исполняемого файла wp1.exe. Он строиться из объектного модуля wp1.obj с помощью библиотеки импорта KERNEL32.LIB для доступа к функциям API Windows в указанном выше соглашении на действительные имена таких функций.

Вопциях компоновщика для исполняемого файла wp1.exe указывается, что программа предназначена для выполнения в консольном режиме (опция /SUBSYSTEM:CONSOLE) и что метка начала выполнения программы есть start (опция /ENTRY:start). Заметим, что действительное имя метки начала выполнения программы есть _start, но выше уже объяснялась почему имеется такое расхождение имен.

Для формирования DLL-библиотеки использован один из вариантов задания экспортируемых из библиотеки имен. Этот вариант состоит в использовании опции /EXPORT, за ключевым словом которой через двоеточие следует имя экспортируемой функции - в форме, принятой для имен языка Си. В нашем случае опция указывается в виде /EXPORT:wiwoda, хотя действительное имя функции (видимое и в объектном файле) есть _wiwoda.

Третья команда командного файла на рис. 8.3.3 может быть заменена на команду link /DLL /DEF:wp2.def wp2.obj kernel32.lib

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

EXPORTS wiwoda

но может состоять и только из второй строки с оператором EXPORTS, так как оператор LIBRARY для компоновщика фирмы MS не является обязательным.

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

198

link /DLL wp2.obj kernel32.lib @ wwpp.lnk

Этот файл wwpp.lnk может содержать, например, текст /export:wiwoda

/export:funa

/export:abc

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

Когда исполняемый файл создается из исходного файла wp1.c на языке Си и должен вызывать функции из DLL-библиотеки wp2.DLL, для компоновки может быть использован командный вызов

link /subsystem:console /entry:main wp1.obj kernel32.lib wp2.lib

(При этом предполагается, что объектный файл получен вызовом транслятора cl -c wp1.c.) Текст такого файла приведен на рис. 8.3.4. Заметим, что при использовании исходного языка Си имена экспортируемых функций в этой программе и используемых данных экспорта совпадают (в данном случае вместо библиотеки можно было использовать опцию /export:wiwoda).

#include <windows.h> extern int wiwoda(char* text); void main()

{int k=1; HANDLE hout; DWORD act;

char ttt[ ]="Text for dll-MS";

hout=GetStdHandle(STD_OUTPUT_HANDLE); WriteFile(hout,"\nBegin prog\n",12,&act,NULL); wiwoda(ttt);

WriteFile(hout,"\nEnd prog\n",10,&act,NULL); Sleep(2000);

}

Рис. 8.3.4. Программа wp1.c для использования функции wiwoda из DLL, полученной из wp1.asm

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

cl wp1.c wp2.lib

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

cl имяисх.c /link /DLL /EXPORT:имя_экпортируемое

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

199

EXE. Чтобы добиться обычного в практике обозначения DLL библиотеки, следует предыдущий вызов задавать в несколько измененном виде, приведенном в следующей строке.

cl /Feимяdll.dll имяисх.c /link /DLL остальные опции и параметры

В частности из исходного файла wp3.c, приведенного на рис. 8.3.5, библиотека wp3.DLL может быть создана вызовом

cl /Fewp3.dll wp3.c /link /DLL /EXPORT:wiwoda

Заметим, что при использовании системы разработки MS для построения программ с языка Си требуется не только исполняемые файлы транслятора CL.EXE и компоновщика LINK.EXE, но и файлы C2.EXE, C1.DLL, C1XX.DLL, MSPDB50.DLL, MSPDB60.DLL, MSVCRT.DLL.

#include <windows.h> #include <stdio.h>

char attr=0x1e, attrr=0x0c, attrg=0x0a; void wiwoda(char *txt)

{HANDLE hstdout; COORD coor; DWORD cbwritten;

hstdout=GetStdHandle(STD_OUTPUT_HANDLE); coor.X=35; coor.Y=12; WriteConsoleA(hstdout,"Privetaa...",11,&cbwritten,NULL); FillConsoleOutputAttribute(hstdout, attrr, 12, coor, &cbwritten);

WriteConsoleOutputCharacterA(hstdout, txt, 12, coor, &cbwritten);

}

Рис. 8.3.5. Файл wp3.c для библиотеки DLL

Кроме того, потребуются библиотеки KERNEL32.LIB, LIBC.LIB, MSVCRT.LIB, UUID.LIB, OLDNAMES.LIB и содержимое каталога INCLUDE, где содержатся необходимые заголовочные файлы. Причем при использовании усеченной системы разработки, состоящей только из указанных файлов, необходимо задать путь доступа к заголовочным файлам и библиотекам, что можно сделать выполнением команд установки констант окружения, имеющими вид

SET INCLUDE=путь_к_каталогу_INCLUDE SET LIB=путь_к_каталогу\LIB

(Возможно также указание путей этих каталогов в опциях /Iпуть и /LIBPATH:путь при вызове программ CL.EXE и LINK.EXE соответственно.)

8.4. Динамическая компоновка времени выполнения

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

200

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