гос / sp-lect (1)
.pdfЗмінна – унікальне у межах підпрограми символьне ім’я локальної змінної підпрограми, а тип змінної будь який простий тип або попередньо визначений структурований тип.
При великій кількості локальних змінних для перенесення переліку змінних на наступний рядок використовується символ «\» після коми або декілька директив LOCAL.
Використання розширених можливостей синтаксичного опису підпрограми забезпечує автоматичне дотримання конвенцій виклику та іменування, що, зокрема, забезпечується генерацією асемблером «прихованого» коду прологу та епілогу підпрограми.
Наприклад, для підпрограми визначеної як
FunctonName PROC STDCALL PUBLIC USES esi edi ebx p1:DWORD, p2:DWORD LOCAL v1:BYTE, v2:WORD, v3:DWORD
LOCAL v4[20]:BYTE mov eax,p1 mov ebx,p2 mov v1,1 mov v2,2 mov v3,3
ret FunctonName ENDP
Асемблер генерує наступний код
; Пролог підпрограми (наступні 3 команди еквівалентні команді ENTER 28,0)
push ebp |
; Зафіксувати стан кадру стеку для |
mov ebp, esp |
; подальшого доступу до параметрів |
sub esp, 28 |
; Зарезервувати місце для локальних |
; змінних визначених директивами LOCAL (27 байт і вирівнювання стеку)
push esi
push edi ; USES esi edi ebx
push ebx
; Код підпрограми
mov eax, ss:[ebp+8]
mov ebx, ss:[ebp+12] однак для PASCAL по іншому – як?)
mov byte ptr ss:[ebp-1],1 |
; Локальні змінні |
||
mov word ptr ss:[ebp-4],2 |
; розміщуються |
||
mov dword ptr ss:[ebp-8],3 |
; в стеку |
||
; Епілог підпрограми |
|
||
pop ebx |
|
|
|
pop edi |
|
|
|
pop esi |
|
|
|
mov esp, ebp |
Команда |
|
|
pop ebp |
|
LEAVE |
|
ret 8 |
; STDCALL (так само для PASCAL, |
;однак для С по іншому – як?)
Зрозширеного синтаксичного опису підпрограми видно, що MASM, безпосередньо не підтримує, як це не дивно, найбільш «асемблерну» з усіх конвенцій виклику FASTCALL. Однак, зовсім не обов’язково використовувати повні можливості розширеного синтаксису опису підпрограми, і на асемблері можна «під лаштуватися» під будь яку конвенцію виклику, сформувавши за необхідності потрібний код прологу та епілогу «вручну» – те, що неможливо
написати на асемблері, не можливо написати взагалі!
Певні перешкоди створюють не підтримувані безпосередньо конвенції іменування, але тут на допомогу приходить підтримувана MASM конвенція виклику і іменування SYSCALL, яка не передбачає модифікації визначеного в описі підпрограми імені функції при формуванні її внутрішнього імені. Це дозволяє при описі підпрограм призначати їм імена, що співпадають з внутрішніми іменами, які хоче бачити С-компілятор.
Наприклад, функцію з прототипом int __fastcall FunctionName(int x, int y) компілятор Microsoft Visual C++буде шукати за іменем @FunctionName@8.
Тоді опис відповідної підпрограми на асемблері буде виглядати наступним чином:
@FunctionName@8 |
PROC SYSCALL PUBLIC USES esi edi ebx |
; В регістрі ecx параметр x, в edx – параметр y <код підпрограми>
ret
@FunctionName@8 ENDP
В цьому прикладі кількість параметрів не передбачає використання стеку, однак слід зазначити, що і в цьому випадку при формування внутрішнього імені використовується декорація пост суфіксом @8. При більшій кількості параметрів їх опис потрібно розпочинати з третього, ім’я функції завершити пост суфіксом @N, де N дорівнює загальному розміру усіх параметрів функції.
Порядок параметрів в стеку для SYSCALL співпадає з порядком прийнятим для __fastcall (і __cdecl), асемблер для SYSCALL побудує коректний стековий кадр для звертання до параметрів за іменами зазначеними в описі. Завершити підпрограму доведеться трохи порушивши SYSCALL командою RET K, де K дорівнює загальному розміру параметрів функції в стеку.
7.5 Статичні бібліотеки
7.5.1 Структура вихідного коду статичних бібліотек
Каркасний код статичної бібліотеки має наступний вигляд
.386
.model flat [,<конвенція>]
option casemap:none
.code
<визначення підпрограми>
………………….
end
Всі призначені для використання поза межами модуля (експортовані) функції статичної бібліотеки повинні визначатися з атрибутом видимості PUBLIC або за допомогою окремої однойменної директиви: PUBLIC і’мя_функції.
Трансляція вихідного тексту майбутньої бібліотеки виконується звичайним способом
ML.EXE /c /coff /Cp /nologo /I"C:\MASM32\INCLUDE" ім’я.asm
Подальше отримання бібліотечного lib-файлу виконується за допомогою спеціального параметра лінкера /LIB
LINK.EXE /LIB /LIBPATH:"C:\MASM32\LIB" ім’я.obj
або з використанням спеціальної утиліти Microsoft Library Manager LIB.EXE /LIBPATH:"C:\MASM32\LIB" ім’я.obj
Приклад 7.1 Статична бібліотека slldemo.lib в якій реалізовано альтернативні і розширені варіанти стандартних функцій С для обробки рядків. Зокрема, стандартна С-функція strcat вміє об’єднувати тільки два рядки, а її розширений бібліотечний аналог astrcat – довільну кількість. Деякі функції мають як __cdecl так і __fastcall реалізації, а аналог функції strlen реалізовано згідно з конвенцією __stdcall.
; Файл slldemo.asm
.486
.model flat
option casemap:none
.code
StringLength |
proc PRIVATE uses ecx edi |
;Внутрішня функція для визначення довжини ASCIIZ-рядка
;Передавання параметрів регістрове.
; Вхід: EDI – адреса рядка, Вихід: EAX – довжина
; Внутрішня функція зберігає ВСІ регістри, які використовує
mov ecx,-1
xor al,al
repnz scasb
neg ecx
lea eax,[ecx-2]
ret
StringLength endp
astrlen proc STDCALL PUBLIC uses edi lpstr:DWORD
;size_t strlen(const char* String)
mov edi,lpstr call StringLength ret
astrlen endp
amemcpy proc C PUBLIC uses esi edi ebx lpdst:DWORD,\ lpsrc:DWORD, n:DWORD
;void *memcpy(void *dst, const void *src, size_t n)
mov edi,lpdst mov esi,lpsrc mov ecx,n mov ebx,ecx shr ecx,2 rep movsd mov ecx,ebx and ecx,3 rep movsb ret
amemcpy endp
astrcpy proc C PUBLIC uses esi edi lpdst:DWORD, lpsrc:DWORD mov edi,lpsrc
call StringLength
lea ecx,[eax+1] ; 0 для завершення рядка
mov esi,edi mov edi,lpdst shr ecx,2 rep movsd
lea ecx,[eax+1] and ecx,3
rep movsb ret
astrcpy endp
@fcstrcpy@8 |
proc SYSCALL PUBLIC uses esi edi |
; __fastcall-варіант astrcpy.
mov edi,edx
call StringLength mov esi,edi
mov edi,ecx lea ecx,[eax+1] shr ecx,2
rep movsd
lea ecx,[eax+1] and ecx,3
rep movsb ret
@fcstrcpy@8 endp
amemset proc C PUBLIC uses edi ebx lpdest:DWORD, chr:BYTE,\ count:DWORD
;void *memset( void *dest, int c, size_t count );
mov edi,lpdest mov al,chr mov ecx,count mov ah,al mov bx,ax bswap eax mov ax,bx mov ebx,ecx shr ecx,2
rep stosd mov ecx,ebx and ecx,3 rep stosb ret
amemset endp
@fcmemset@12 proc SYSCALL PUBLIC uses edi ebx count:DWORD
;void *memset( void *dest, int c, size_t count );
;lpdest=ECX, chr=DL (EDX),
mov edi,ecx mov al,dl mov ecx,count mov ah,al mov bx,ax bswap eax mov ax,bx mov ebx,ecx shr ecx,2
|
rep stosd |
|
mov ecx,ebx |
|
and ecx,3 |
|
rep stosb |
|
ret 4 |
@fcmemset@12 endp |
|
astrcat |
proc C PUBLIC uses esi edi ebx strcount:DWORD, lpstrlist:VARARG |
;Виконує конкатенацію декількох ASCIIZ-рядків, приєднуючи їх до першого
;Вхід: strcount – кількість рядків, що буде приєднуватися до першого,
;тобто при виклику з N параметрами strcount=N-1
; lpstrlist – перелік адрес рядків, перший елемент – адреса рядка-приймача
; Вихід: немає (EAX==0)
lea edx,lpstrlist mov edi,[edx] call StringLength lea edi,[edi+eax] mov ebx,strcount jmp zero
next: lea edx,[edx+4] mov esi,[edx]
@@: lodsb
stosb or al,al jnz @B dec edi dec ebx
zero: or ebx,ebx jnz next stosb
ret astrcat endp End
7.5.2 Зв’язування MASM із статичними бібліотеками
Для зв’язування проекту Microsoft Macro Assembler із статичною бібліотекою потрібно додати ім’я файлу бібліотеки в командний рядок лінкера або скористатися директивою INCLUDELIB. При лінкуванні проекту файл бібліотеки повинен розміщуватися в каталозі проекту, або в стандартних каталогах, в яких лінкер виконує пошук бібліотек, або зазначатися за повним ім’ям.
Після цього потрібно виконати опис прототипів використовуваних функцій бібліотеки у вихідному тексті асемблерної програми у вигляді:
ім’я PROTO [<конвенція>] <тип_параметрів>
<тип_параметрів> являє собою розділений комою список записів виду :тип, де тип – співпадає з типом параметрів функції. Символьні імена параметрів зазначати не обов’язково.
Після визначення прототипів виклик функцій виконується за допомогою директиви з синтаксисом високого рівня INVOKE. Ця директива генерує послідовність з команд PUSH для розміщення параметрів в стеку, команди CALL для виклику підпрограми і команди для вирівнювання стеку, якщо це передбачається конвенцією виклику. Наприклад, для функцій з прототипами
FuctionName1 PROTO C :DWORD, :DWORD
FuctionName2 PROTO STDCALL :DWORD, :DWORD
FuctionName3 PROTO PASCAL :DWORD, :DWORD
Виклики з використанням директиви INVOKE
invoke FuctionName1,x,y
invoke FuctionName2,x,y
invoke FuctionName3,x,y
перетворюються трансляторм, відповідно на
push y push x
call _ FuctionName1 add esp,8
push y push x
call _FuctionName2@8
push x push y
call FUCTIONNAME3
Для конвенцій, що безпосередньо не підтримуються MASM, прототипи функцій визначити не можливо і замість прототипів використовуються директиви EXTRNDEF або EXTRN. При цьому виклик функції за допомогою
INVOKE стає не можливим і потрібно вручну сформувати код виклику згідно з з конвенцією виклику.
Приклад 7.2 Додаток на асемблері slldemocall.exe, що демонструє використання бібліотеки slldemo.lib (див. Приклад 7.1).
; Файл slldemocall.inc
include kernel32.inc
include masm32.inc
include c:\masm32\Macros\Strings.mac
includelib |
kernel32.lib |
; ExitProcess і потрібна для masm32.lib |
includelib |
masm32.lib |
; StdOut,StdIn,dwtoa |
includelib |
slldemo.lib |
|
astrlen |
PROTO STDCALL lpstr:DWORD |
amemcpy |
PROTO C lpdst:DWORD, lpsrc:DWORD, n:DWORD |
; імена параметрів в прототипі необов'язкові |
|
astrcpy |
PROTO C :DWORD, :DWORD |
amemset |
PROTO C lpdest:DWORD, chr:BYTE, count:DWORD |
astrcat |
PROTO C strcount:DWORD, lpstrlist:VARARG |
externdef syscall @fcstrcpy@8:proc externdef syscall @fcmemset@12:proc
.data
str1
str2
str3
.data? buffer
; Файл slldemocall.asm
.386
.model flat, stdcall option casemap:none include slldemocall.inc
.code
start: lea edi,buffer lea esi,str1
invoke astrcpy,edi,esi
invoke StdOut,$CTA0("\nstrcpy:\t\t") invoke StdOut,edi
invoke astrcat,2,edi,addr str2,addr str3 invoke StdOut,$CTA0("\nstrcat:\t\t") invoke StdOut,edi
invoke amemset,edi,'x',15
invoke StdOut,$CTA0("\nmemset:\t\t") invoke StdOut,edi
mov ecx,edi mov dl,'y'