Міністерство освіти, науки, молоді та спорту України
Національний університет “Львівська політехніка”
Кафедра ******
Звіт
до лабораторної роботи №6
з дисципліни: «Системне програмування»
на тему: “СТВОРЕННЯ DLL ТА ЇХ ВИКОРИСТАННЯ ПРИ ЯВНОМУ
ЗВ’ЯЗУВАННІ НА МОВІ АСЕМБЛЕР”
Підготував:
***********
Прийняв:
***********
Львів 2015
Мета: Ознайомитись з технологією та оволодіти навиками створення та використання бібліотек динамічного компонування з використанням явного зв’язування на мові Асемблера.
ТЕОРЕТИЧНІ ВІДОМОСТІ
Динамічне компонування образу задачі в процесі її виконання надає ряд переваг розробникам програмного забезпечення в порівнянні зі статичним копонуванням. До переваг відносяться:
- зменшується розмір виконуваного файлу;
- у пам’ять завантажують лише одну копію динамічноїбібліотеки;
- оновлення бібліотек не веде до перекомпонування застосування;
- реалізовується динамічне завантаження модулів на вимогу;
- можливість спільно використовувати ресурси застосування;
- можна організувати спільну роботу бібліотек, розроблених із
використанням різних мов програмування.
Однак воно не позбавлене недоліків:
- сповільнює завантаження застосування;
- не ефективно використовуватися зовнішня пам’ять;
- проблема є зворотної сумісності;
- ускладнюється процес інсталювання програмного застосування.
Однак грамонто володіючи технологією створення та підтримки динамічних бібліотек можне зменшити вплив деяких з цих недоліків на програмний продукт, а деякі подолати.
Можливі 2 способи використання динамічних бібліотек. Вони називаються “явним” та “неявним” зв’язуванням. “Явне” та “неявне” зв’язування бібліотеки з програмою мають суттєві відмінності в процесі написання та компіляції програми.
Неявне зв’язування бібліотеки з програмою (Load-time dynamic linking) полягає в тому, що бібліотека (яка міститься у файлі з розширенням .dll) завантажується в пам’ять в момент завантаження програми.
До переваг “неявного” зв’язування в порівнянні з “явним” відноситься:
- простота програмування. Розробник не вникає в проблеми зв’язування
назв функцій з адресами за якими завантажена їх реалізація;
- прогнозованість поведінки застосування. Якщо застосування успішно
завантажено, то усі проблеми перехрестних зв’язків уже вирішено;
Однак у “неявного” зв’язування існує ряд недоліків:
- при відсутності бодай однієї з бібліотек при запуску програми відбудеться
збій та припинення виконання програми;
- значні затрати часу на завантаження та старт застосування, пов’язані з
необхідністю завантаження усіх динамічних бібліотек;
- відсутня можливість вивантаження непотрібних в даний час динамічних
бібліотек;
- на час компонування необхідно мати додаткові файли з прототипами
функцій та бібліотеку імпорту (.lib).
Отже, основними перевагами “явного” зв’язування, є можливість тонко керувати процесами завантаження та вивантаження динамічних бібліотек, а отже і використовуваною пам’яттю, хоча і за рахунок складності програмування. “Явне” зв’язування бібліотеки з програмою (Run-time dynamic linking) полягає в тому, що бібліотека (яка міститься у файлі з розширенням .dll) завантажується в пам’ять в момент часу, що визначається розробником, за допомогою виклику АРІ функцій LoadLibrary або LoadLibraryEX. При успішному виконанні функція повертає адресу точки входу. При відсутності бібліотеки, яку необхідно завантажити, або при помилках її завантаження функція поверне NULL, а сама програма, може продовжити виконання. Звичайно, якщо функції, що містяться у відсутній бібліотеці не є критичними для її подальшої роботи.
Для виклику бібліотечної функції необхідно оголосити вказівник на функцію, та присвоїти йому адресу бібліотечної функції. Для цього необхідно використати АРІ функцію GetProcAddress, яка повертає адресу вказаної їй у параметрі бібліотечної функції.
Після завершення роботи з функціями бібліотек, програмі необхідно вивантажити бібліотеки за допомогою функції FreeLibrary. Для успішної компіляції необхідно мати лише dll файл бібліотеки. Запуск програми відбудеться навіть за відсутності бібліотечного файлу, оскільки його наявність при використанні “явного” зв’язування не перевіряється.
Завдання
Ввести два рядки тексту, вилучити з довшого рядка всі входження коротшого рядка. Вивести новий рядок на екран.
На першому етапі створюємо бібліотеку (файли lab07.dll і lab07.lib) засобами MSVS. Для цього створюємо проект типу DLL, в якому два файли: lab07.def i lab07.asm:
//lab07.def
LIBRARY Lab07
EXPORTS RFunc
//lab07.asm
.586
.model flat, stdcall
option casemap :none
include \masm32\include\windows.inc
include \masm32\macros\macros.asm
; підключення файлів з форматами прототипів виклику функцій
include \masm32\include\masm32.inc
include \masm32\include\gdi32.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
include \masm32\include\advapi32.inc
; підключення заголовків бібліотек експортованих функцій
includelib \masm32\lib\masm32.lib
includelib \masm32\lib\gdi32.lib
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\advapi32.lib
.data
titul db "KhomitsVM @ Labwork #7, asm dll function.", 0
isFound db "Substring found in string!",0
isRez db "Rezult: ",0
lpSrc dword 256 dup(?),0
lpPattern dword 256 dup(?),0
str1 dword 256 dup(?),0
str2 dword 256 dup(?),0
BaseLen DWORD 0
SubLen DWORD 0
str1len dword 0
str2len dword 0
nl DB 0AH,0DH,0
.code
DllEntry PROC hInstDLL: DWORD, reason: DWORD, reserved:DWORD
mov eax,1
ret
DllEntry ENDP
RFunc PROC
pusha
invoke StdOut,addr titul
invoke StdOut,ADDR nl
invoke StdOut,chr$("Input original string: ")
invoke StdIn,addr lpSrc,LengthOf lpSrc
invoke StdOut,chr$("Input replaced string: ")
invoke StdIn,addr lpPattern,LengthOf lpPattern
cld
;обчислення довжин введених рядків символів
lea edi, lpSrc
mov esi,edi
mov ecx, 0FFFFFFFFH
xor al,al
repne scasb
sub edi,esi
dec edi
mov BaseLen, edi
lea edi, lpPattern
mov esi,edi
mov ecx, 0FFFFFFFFH
xor al,al
repne scasb
sub edi, esi
dec edi
mov SubLen, edi
;порівняння довжин рядків
mov ebx,SubLen
cmp BaseLen,ebx
jb s2s1
;копіюємо довший рядок в змінну str1(а його довжину в str1len),
;аналогічно для коротшого рядка str2 (str2len)
cld
mov ecx,BaseLen
lea esi,lpSrc
lea edi,str1
rep movsb
mov ecx,SubLen
lea esi,lpPattern
lea edi,str2
rep movsb
mov ebx,BaseLen
mov str1len,ebx
mov ebx,SubLen
mov str2len,ebx
mov edi,str2len
jmp s1s2
s2s1:
cld
mov ecx,SubLen
lea esi,lpPattern
lea edi,str1
rep movsb
mov ecx,BaseLen
lea esi,lpSrc
lea edi,str2
rep movsb
mov ebx,SubLen
mov str1len,ebx
mov ebx,BaseLen
mov str2len,ebx
mov edi,str2len
;завершення блоку визначення довшого і коротшого рядків
;їхні дані відповідно записані в str1 (str1len) i str2 (str2len)
s1s2:
mov ebx,0
lea esi, str1
mainloop:
lea edi, str2
.if ebx==str1len
jmp toexit
.endif
push esi
cld
mov ecx, [str2len]
repe cmpsb
jne next
invoke StdOut,addr isFound
invoke StdOut,ADDR nl
mov edi ,esi
sub edi, str2len
mov edx,str1len
sub edx, ebx
sub edx,str2len
mov ecx,edx
inc ecx
rep movsb
next:
pop esi
inc esi
inc ebx
jmp mainloop
toexit:
invoke StdOut,addr isRez
invoke StdOut,addr str1
invoke StdOut,ADDR nl
invoke Sleep,10000
popa
ret
RFunc ENDP
End DllEntry
Після компіляції отримаємо бібліотеку (файли lab07.dll і lab07.lib, яка містить одну функцію: RFunc (без аргументів), яка вилучає з одного рядка інший (з довшого коротший).
На другому етапі будуємо проект консольного типу, пишемо програму, яка дозволяє викликати функцію RFunc з допомогою засобів явного звязування з бібліотеки lab07.dll (використавши lab07.lib). Для цього два файли, lab07.dll і lab07.lib, розміщуємо в папці основного проекту.
Код програми:
//laba6.cpp
.586
.model flat,stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\macros\macros.asm
; підключення файлів з форматами прототипів виклику функцій
include c:\masm32\include\masm32.inc
include c:\masm32\include\gdi32.inc
include c:\masm32\include\user32.inc
include c:\masm32\include\kernel32.inc
include c:\masm32\include\advapi32.inc
; підключення заголовків бібліотек експортованих функцій
includelib c:\masm32\lib\masm32.lib
includelib c:\masm32\lib\gdi32.lib
includelib c:\masm32\lib\user32.lib
includelib c:\masm32\lib\kernel32.lib
includelib c:\masm32\lib\advapi32.lib
.data
LibName db "Lab07.dll",0
FunctionName db "RFunc",0
DllNotFound db "Cannot load library",0
AppName db "Load explisit library",0
NotFound db "StrFunct function not found",0
.data?
hLib dd ?
RFuncAddr dd ?
.code
start:
invoke LoadLibrary, addr LibName
.if eax==NULL
invoke MessageBoxA, NULL, addr DllNotFound, addr AppName, MB_OK
.else
mov hLib, eax
invoke GetProcAddress, hLib, addr FunctionName
.if eax == NULL
invoke MessageBoxA, NULL,addr NotFound, addr AppName, MB_OK
.else
mov RFuncAddr,eax
call [RFuncAddr]
.endif
invoke FreeLibrary,hLib
.endif
invoke ExitProcess, NULL
end start
Детальний опис роботи коду
В Асемблері рядки є послідовностями байтів, які можуть або представляти, або не представляти ASCII-символи. Рядкові команди діляться на три групи:
- команди пересилки рядків
- команди перевірки рядків
- команди префікса повтору.
Для вводу рядків використовуємо стандартні засоби MASM invoke StdOut, invoke StdIn. Далі функція обчислює довжини введених рядків та порівнює їх. За результатами перевірки вибираємо довший рядок і в ньому здійснюємо пошук вкладення коротшого рядка.
Якщо вкладення є, то шляхом використання рядкових команд дописуємо «хвіст» рядка в позицію, де починалось вкладення. Таким чином отримуємо ефект видалення коротшого рядка з довшого. У рядку, що отримали після видалення, знову здійнюємо пошук вкладення коротшого рядка. Процес повторюємо до проходження всіх символів рядка (для оптимізації роботи можна було б проходити не всі символи – використати момент, що залишок довшого рядка для пошуку не може бути коротшим за короткий рядок). Результат роботи виводиться функцією в консоль.
Для роботи використано наступні команди обробки рядків: lea - запис ефективної адреси в регістр; repne scasb - сканування рядка, поки не нуль; rep movsb – копіювання ланцюжка (рядка) символів, допоки в регістрі ecx не нуль; repe cmpsb – команда порівняння двох рядків, допоки в регістрі ecx не нуль. Префікси rep, repe, repne – означають повтор рядкових команд, допоки в регістрі ecx не нуль. Попередньо в регістер ecx заноситься необхідне число повторів (згідно логіки програми).
Після завершення роботи бібліотечної функції повертаємось в основну програму і завершуємо роботу.
Результат виконання програми:
Варіант, коли перший введений рядок довший за другий
/
Варіант, коли другий введений рядок довший за перший
/
Висновок: на цій лабораторній роботі я навчилась створювати бібліотеки та використовувати виклики функцій з цих бібліотек при явному звязуванні.