Programovanie v assembleri vo Windows x64 (x86-64): Rozdiel medzi revíziami

Smazaný obsah Přidaný obsah
Fabcde (diskusia | príspevky)
d zalamovanie
Značka: editor wikitextu 2017
Fabcde (diskusia | príspevky)
printf
Riadok 31:
; golink /console /ni /entry main HelloWorld.obj kernel32.dll
; alternativne linkovanie:
; ld -e main -s HelloWorld.obj -o HelloWorld.exe HelloWorld.obj c:\windows\system32\kernel32.dll
 
 
Riadok 131:
 
'''Výpis 1b''' HelloWorld.s (Verzia pre GAS):
<syntaxhighlight lang="asgas" line="1">
# HelloWorld.s
 
# kompilacia:
# as HelloWorld.s -o HelloWorld.o HelloWorld.s
# linkovanie:
# ld -e main -s HelloWorld.o -o HelloWorld.exe HelloWorld.o c:\windows\system32\kernel32.dll
# alternativna kompilacia+linkovanie:
# gcc -m64 -nostartfiles -Wl,-emain -o HelloWorld.exe HelloWorld.s c:\windows\system32\kernel32.dll
Riadok 151:
 
main:
/*# rax = GetStdHandle(-11) */
/*# HANDLE hStdHandle = WINAPI GetStdHandle (_In_ DWORD nStdHandle) */
/*# nStdHandle: STD_INPUT_HANDLE=-10 , STD_OUTPUT_HANDLE=-11 , STD_ERROR_HANDLE=-12 */
mov $-11, %ecx #/* 1. param _In_ DWORD nStdHandle */
call GetStdHandle
 
/*# rax = WriteFile(%rax, $message, $MESSAGE_LEN, %rsp-4, 0) */
/*# BOOL bErrorFlag = WINAPI WriteFile (_In_ HANDLE hFile, _In_ LPCVOID lpBuffer, _In_ DWORD nNumberOfBytesToWrite, _Out_opt_ LPDWORD lpNumberOfBytesWritten, _Inout_opt_ LPOVERLAPPED lpOverlapped) */
/*# WriteConsole(handle, &msg[0], 13, &written, 0) */
mov %rax, %rcx #/* 1. param _In_ HANDLE hFile */
mov $message, %rdx #/* 2. param _In_ LPCVOID lpBuffer */
mov $MESSAGE_LEN, %r8d #/* 3. param _In_ DWORD nNumberOfBytesToWrite */
mov $lpNumberOfBytesWritten, %r9 #/* 4. param _Out_opt_ LPDWORD lpNumberOfBytesWritten */
pushq $0 #/* 5. param _Inout_opt_ LPOVERLAPPED lpOverlapped */
call WriteFile
add $8, %rsp #/* uvolnenie miesta v zasobniku */
 
/*# ExitProcess(0) */
/*# VOID WINAPI ExitProcess( _In_ UINT uExitCode) */
mov $0, %ecx #/* 1. param _In_ UINT uExitCode UINT je 32 bit aj v 64 bitovom prostredi */
call ExitProcess
 
Riadok 296:
; golink /console /ni /entry main HelloWorld.obj kernel32.dll
; alternativne linkovanie:
; ld -e main -s HelloWorld.obj -o HelloWorld.exe HelloWorld.obj c:\windows\system32\kernel32.dll
 
 
Riadok 351:
xor ecx, ecx ; 1. param _In_ UINT uExitCode UINT je 32 bit aj v 64 bitovom prostredi
</syntaxhighlight>
Je to pokus o optimalizáciu kódu, kedže inštrukcia xor ecx,ecx tiež vynuluje obsah registra ECX, ale po preložení zaberá menej bajtov. Optimalizácia kódu je však dnes kvôli prúdovému spracovaniu inštrukcií ([[w:cs:Pipelining|pipelining]]), hypertredinguhyper-threading, cache pamäti, atď mimoriadne zložitá a vyžaduje hlboké znalosti. Spravidla dobrý kompilátor/optimalizátor jazyka C dokáže vytvoriť rýchlejší kód než hoci aj kratší ale neoptimalizovaný kód v jazyku symbolických adries.<ref>https://www.zive.cz/clanky/vyznejte-se-v-procesoru--velky-prehled-technologii/historie-procesoru-cache-a-skalarni-procesory/sc-3-a-147124-ch-66129/default.aspx#articleStart</ref><ref>http://frdsa.fri.uniza.sk/~janosik/Kniha/Prudove_sprac.html</ref><ref>https://forum.root.cz/index.php?topic=2388.0</ref>
 
'''Výpis 2b''' HelloWorld.s (Verzia pre GAS):
<syntaxhighlight lang="asgas" line="1">
# HelloWorld.s
 
# kompilacia:
# as HelloWorld.s -o HelloWorld.o HelloWorld.s
# linkovanie:
# ld -e main -s HelloWorld.o -o HelloWorld.exe HelloWorld.o c:\windows\system32\kernel32.dll
# alternativna kompilacia+linkovanie:
# gcc -m64 -nostartfiles -Wl,-emain -o HelloWorld.exe HelloWorld.s c:\windows\system32\kernel32.dll
Riadok 374:
 
main:
sub $0x28, %rsp #/* rezervovanie miesta v zasobniku pre 5-ty argument, shadow space a zarovnanie
 
/*# rax = GetStdHandle(-11) */
/*# HANDLE hStdHandle = WINAPI GetStdHandle (_In_ DWORD nStdHandle) */
/*# nStdHandle: STD_INPUT_HANDLE=-10 , STD_OUTPUT_HANDLE=-11 , STD_ERROR_HANDLE=-12 */
mov $-11, %ecx #/* 1. param _In_ DWORD nStdHandle */
call GetStdHandle
 
/*# rax = WriteFile(%rax, $message, $MESSAGE_LEN, %rsp-4, 0) */
/*# BOOL bErrorFlag = WINAPI WriteFile (_In_ HANDLE hFile, _In_ LPCVOID lpBuffer, _In_ DWORD nNumberOfBytesToWrite, _Out_opt_ LPDWORD lpNumberOfBytesWritten, _Inout_opt_ LPOVERLAPPED lpOverlapped) */
/*# WriteConsole(handle, &msg[0], 13, &written, 0) */
mov %rax, %rcx #/* 1. param _In_ HANDLE hFile */
mov $message, %rdx #/* 2. param _In_ LPCVOID lpBuffer */
mov $MESSAGE_LEN, %r8d #/* 3. param _In_ DWORD nNumberOfBytesToWrite */
mov $lpNumberOfBytesWritten, %r9 #/* 4. param _Out_opt_ LPDWORD lpNumberOfBytesWritten */
movq $0, 0x20(%rsp) #/* 5. param _Inout_opt_ LPOVERLAPPED lpOverlapped */
call WriteFile
 
/*# ExitProcess(0) */
/*# VOID WINAPI ExitProcess( _In_ UINT uExitCode) */
xor %ecx, %ecx #/* 1. param _In_ UINT uExitCode UINT je 32 bit aj v 64 bitovom prostredi */
call ExitProcess
 
add $0x28, %rsp #/* uvolnenie rezervovaneho miesta */
 
</syntaxhighlight>Za povšimnutie stojí zápis nepriamej adresácie (používanej napríklad pri indexovaní poľa). Kým v NASM to bolo: <code>mov qword [rsp + 20h], 0</code>, v GAS je "index" uvedený pred zátvorkou: <code>movq  $0, 0x20(%rsp)</code>
Riadok 412:
 
GAS:  <code>mov  (%rbp, %rsi, 1), %eax</code>
 
=== Hello, World! v.3 ===
Posledná verzia programu Hello, World vypíše text pomocou funkcie printf štandardnej knižnce jazyka C.
 
'''Výpis 3a''' HelloWorld.asm:<syntaxhighlight lang="nasm" line="1">
; HelloWorld.asm
 
; kompilacia:
; nasm -f win64 HelloWorld.asm
; linkovanie:
; ld -e main -s -o HelloWorld.exe HelloWorld.obj C:\opt\mingw64\x86_64-w64-mingw32\lib\libmsvcrt.a
; alternativne linkovanie:
; gcc -nostartfiles -Wl,-emain,-s -o HelloWorld.exe HelloWorld.obj C:\opt\mingw64\x86_64-w64-mingw32\lib\libmsvcrt.a
 
 
global main
 
extern printf
 
 
section .text use64 ; Program code
message: db "Hello, World!",0xd,0xa,0
 
main:
sub rsp, 28h ; rezervovanie miesta v zasobniku pre shadow space a zarovnanie
 
; int printf(const char *format, ...)
; rax = printf(message)
mov rcx, qword message
call printf
 
add rsp, 28h ; uvolnenie rezervovaneho miesta
 
ret
 
</syntaxhighlight>Volanie funkcie printf v 64-bitovom windows tiež používa volaciu konvenciu Microsoft x64<ref>https://en.wikipedia.org/wiki/X86_calling_conventions#List_of_x86_calling_conventions</ref>.
 
== Rozširujeme ==
Řádek 422 ⟶ 458:
Napríklad vydelením čísla 321 : 10 dostaneme neúplný podiel 32, zvyšok je 1. Pokračujeme v delení s neúplným podielom 32 : 10 = 3, zvyšok 2, a nakoniec 3 : 10 = 0, zvyšok 3. Zvyšok postupne obsahoval jednotlivé cifry 1, 2, 3, ktoré musíme previesť na zodpovedajúci ASCII znak.
 
'''Výpis 3a4a''' IntToStr.asm:<syntaxhighlight lang="nasm" line="1">
; IntToStr.asm
 
Řádek 430 ⟶ 466:
; golink /console /ni /entry main IntToStr.obj kernel32.dll
; alternativne linkovanie:
; ld -e main -s IntToStr.obj -o IntToStr.exe IntToStr.obj c:\windows\system32\kernel32.dll
 
 
Řádek 494 ⟶ 530:
 
=== IntToHex ===
'''Výpis 4b5b''' IntToHex.s:<syntaxhighlight lang="asgas" line="1">
# IntToHex.s
 
# kompilacia:
# as IntToHex.s -o IntToHex.o IntToHex.s
# linkovanie:
# ld -e main -s IntToHex.o -o IntToHex.exe IntToHex.o c:\windows\system32\kernel32.dll
# alternativna kompilacia+linkovanie:
# gcc -m64 -nostartfiles -Wl,-emain -o IntToHex.exe IntToHex.s c:\windows\system32\kernel32.dll
Řádek 507 ⟶ 543:
 
.section .data
buffer: .ascii " " #/* Najvacsie 64-bitove cislo bez znamienka ma v setnastkovej sustave 16 cifier (2**64 - 1 = ffffffffffffffff) */
enter: .ascii "h\r\n"
lpNumberOfBytesWritten: .long 0
Řádek 519 ⟶ 555:
 
IntToHex:
mov $1234567890, %rax #/* cislo, ktore potrebujeme vypisat (delenec) */
mov $16, %rbx #/* zaklad ciselnej sustavy (delitel) */
 
lea (buffer+BUFFER_LEN-1), %rdi #/* nastavi register rdi na koniec buffera (cifry budeme ziskavat smerom od najnizsieho radu k najvyssiemu) */
 
vydel:
xor %rdx, %rdx #/* pred delenim je nutne rdx vynulovat, inak delenie skonci chybou */
div %rbx #/* vydeli rax / rbx, podiel vlozi do rax, zvysok do rdx */
 
cmp $10, %dl #/* zistime, ci zvysok je mensi nez 10 */
jl doDesat
add $7, %dl #/* medzi znakom '9' a 'A' lezi v ASCII sedem znakov, ktore potrebujeme pri prevode na znak 'A'-'F' preskocit */
 
doDesat:
add $'0', %dl #/* pripocitanim 30h prevedie cislo 0-9 na znak '0'-'9' */
mov %dl, (%rdi) #/* ulozi ziskanu cifru do buffera */
sub $1, %rdi #/* posunie ukazovatel na dalsi rad */
or %rax, %rax
jnz vydel #/* opakuje, kym neziska vsetky cifry */
 
vypis:
sub $0x28, %rsp #/* rezervovanie miesta v zasobniku pre 5-ty argument, shadow space a zarovnanie */
 
/*# rax = GetStdHandle(-11) */
/*# HANDLE hStdHandle = WINAPI GetStdHandle (_In_ DWORD nStdHandle) */
/*# nStdHandle: STD_INPUT_HANDLE=-10 , STD_OUTPUT_HANDLE=-11 , STD_ERROR_HANDLE=-12 */
mov $-11, %ecx #/* 1. param _In_ DWORD nStdHandle */
call GetStdHandle
 
/*# rax = WriteFile(%rax, $buffer, $BUFFER_LEN, %rsp-4, 0) */
/*# BOOL bErrorFlag = WINAPI WriteFile (_In_ HANDLE hFile, _In_ LPCVOID lpBuffer, _In_ DWORD nNumberOfBytesToWrite, _Out_opt_ LPDWORD lpNumberOfBytesWritten, _Inout_opt_ LPOVERLAPPED lpOverlapped) */
/*# WriteConsole(handle, &msg[0], 13, &written, 0) */
mov %rax, %rcx #/* 1. param _In_ HANDLE hFile */
mov $buffer, %rdx #/* 2. param _In_ LPCVOID lpBuffer */
mov $BUFFER_LEN+NEWLINE_LEN, %r8d #/* 3. param _In_ DWORD nNumberOfBytesToWrite */
mov $lpNumberOfBytesWritten, %r9 #/* 4. param _Out_opt_ LPDWORD lpNumberOfBytesWritten */
movq $0, 0x20(%rsp) #/* 5. param _Inout_opt_ LPOVERLAPPED lpOverlapped */
call WriteFile
 
/*# ExitProcess(0) */
/*# VOID WINAPI ExitProcess( _In_ UINT uExitCode) */
xor %ecx, %ecx #/* 1. param _In_ UINT uExitCode UINT je 32 bit aj v 64 bitovom prostredi */
call ExitProcess
 
add $0x28, %rsp #/* uvolnenie rezervovaneho miesta */
</syntaxhighlight>Ak chceme vypísať číslo v inej, napríklad šestnástkovej sústave, stačí deliť príslušným základom číselnej sústavy:<syntaxhighlight lang="gas" line="1" start="26">
mov $16, %rbx #/* zaklad ciselnej sustavy (delitel) */
</syntaxhighlight>Tiež je potrebné vysporiadať sa so znakmi ':', ';', '<', '=', '>', '?' a '@', nachádzajúcimi sa v ASCII medzi znakmi '9' a 'A':<syntaxhighlight lang="gas" line="1" start="34">
cmp $10, %dl #/* zistime, ci zvysok je mensi nez 10 */
jl doDesat
add $7, %dl #/* medzi znakom '9' a 'A' lezi v ASCII sedem znakov, ktore potrebujeme pri prevode na znak 'A'-'F' preskocit */
 
doDesat:
add $'0', %dl #/* pripocitanim 30h prevedie cislo 0-9 na znak '0'-'9' */
</syntaxhighlight>