середу, 11 лютого 2015 р.

Nasm - компіляція і лінкування в ОС Windows

 Є миті коли є враження, що opensource чи вільне програмне забезпечення в ОС Windows це костиль над костилями, його роблять так, аби він працював, аби працював. Часто доводиться бачити приклад компіляції під вільні ОС, а в Windows все навпаки. Для прикладу його компіляція і лінкування програми в nasm, про сам компілятор nasm ви можете почитати в вікіпедії, про його плюси та мінуси, історію розвитку:

 Суть питання в чому, після завантаження nasm в Windows у вас є лише компілятор, а потім об'єктний файл потрібно компонувати ( лінкувати ) в формат ехе. Але просто зробити так:

$ nasm -f win32 -o hello.exe hello.asm

Не вийде, все одно nasm створить лиш об'єктний файл, в даному випадку ми створили лиш його не розширенням *.obj, a exe. Іншими словами, ми написали наступне:

$ nasm -f win32 -o hello.obj hello.asm

Є декілька варіантів компіляції і компоновані файлів в Windows:
1. використати Alink
2. MinGW ( GCC для Windows )
3. Binutils з використанням ld ( для зручності я використовую Cygwin )

Почнемо з першого, сам компановщик для Windows можна завантажити тут http://alink.sourceforge.net/
Завантажувати файл alink.zip
Код має виглядати так:

;Compilation program test
;nasm -fobj test.asm
;alink -oPE test.obj // писати так як є без пробілів, бо буде помилка при збірці

import MessageBoxA user32.dll ;Include the dll user32.dll
extern MessageBoxA ; Now calling external symbol without underscore....
import ExitProcess kernel32.dll
extern ExitProcess

section .data
msgme db "Це тест програми на компановщику ALINk!",0 ;Text

section .text use32 CLASS=CODE ;"use32 CLASS=CODE for telling the other linker(Such as alink.exe) that program for 32bit

..start: ; ..start (not _start) for other linker for start of the code

push dword 0x00 ; MB_OK
mov esi,msgme ;esi="[Text]"
push esi ; is now top of the stack, second paramaters title
push dword msgme ; Text
push dword 0 ;Reserve
call [MessageBoxA] ;Call the Function MessageBoxA

push dword 0x00
call [ExitProcess]

Результат:



Так, саме так повинен виглядати код для компановщика alink. Замість _start, ..start, також вказуємо в області секції use32 CLASS=CODE, вказати компановщику, що це код для 32 бітної системи. Також для інших секцій вказується подібним прикладом:
SECTION DATA USE32 CLASS=DATA
SECTION BSS USE32 CLASS=DATA
SECTION CODE USE32 CLASS=CODE

Також можна писати section .text use32, покажемо приклад, але вже для консольного варіанту з API WriteConsole:

; Compilation & link
; nasm -f obj test.asm
; alink -oPE test2.obj -subsys con

extern GetStdHandle
import GetStdHandle kernel32.dll
extern WriteConsoleA
import WriteConsoleA kernel32.dll
extern ExitProcess
import ExitProcess kernel32.dll

section .data USE32
strich: db 'this is text console!!!', 0xA
LenStich: equ $-strich

section .text USE32
..start:
push dword -11 ; -11 вивід на консоль
call [GetStdHandle] ; результат буде в регістрі eax
push dword 0
push dword 0
push dword LenStich
push strich
push eax
call [WriteConsoleA]
push dword 0
call [ExitProcess]

Результат:


2. З alink коротко розібрались, тому переходимо до компановки за допомогою GCC, слід зауважити, що цей метод застосовують коли використовують саме функції С, при цьому краще використовувати Cygwin, там все налаштовано, а якщо просто ставити MinGW то можуть бути помилки

; nasm -fwin32 hello.asm
; gcc hello.obj -o hello.exe
; file name hello.asm compilation & linking obj
global _main
extern _printf
section .data
fmtStr: db 'Linker gcc hello.obj -o hello.exe test', 0xA, 0
section .text
_main:
sub esp, 4 ; Allocate space on the stack for one 4 byte parameter
lea eax, [fmtStr]
mov [esp], eax ; Arg1: pointer to format string
call _printf ; Call printf(3):
add esp, 4 ; Pop stack once
ret
; код взятий з сайту wikibooks

Результат:

3. Розглянемо nasm + виклик win32 функцій

; Compilation & linker
; nasm -fwin32 file.asm
; ld -e _start file.obj -lkernel32 -o file.exe
global _start
 
extern _GetStdHandle@4
extern _WriteConsoleA@20
extern _ExitProcess@4
 
section .data
        str:     db 'hello, world',0xA
        strLen:  equ $-str
 
section .bss
        numCharsWritten:        resb 1
 
section .text
        _start:
 
        push    dword 0         ; Arg5: Unused so just use zero
        push    numCharsWritten ; Arg4: push pointer to numCharsWritten
        push    dword strLen    ; Arg3: push length of output string
        push    str             ; Arg2: push pointer to output string
        push    eax             ; Arg1: push handle returned from _GetStdHandle
        call    _WriteConsoleA@20

        push    dword 0         ; Arg1: push exit code
        call    _ExitProcess@4
; код взятий з сайту wikibooks

 Результат тексту програми буде такий самий , як і в попередній. З особливостей, тут ми вказали nasm що буде вже проміжний файл win32 obj, -e параметром вказали точку входу в програму, це в нашому випадку _start, -l підключили бібліотеку, всі win32 функції відносяться до складу kernel32.dll, тому пишемо -lkernel32.

Як бачимо все залежить від від ОС, компановщика. Вдалого вам лінкування.





Немає коментарів:

Дописати коментар