Saya ingin menulis sesuatu yang mendasar dalam perakitan di bawah Windows, saya menggunakan NASM, tetapi saya tidak bisa mendapatkan apa pun yang berfungsi.
Bagaimana cara menulis dan mengkompilasi hello world tanpa bantuan fungsi C di Windows?
Saya ingin menulis sesuatu yang mendasar dalam perakitan di bawah Windows, saya menggunakan NASM, tetapi saya tidak bisa mendapatkan apa pun yang berfungsi.
Bagaimana cara menulis dan mengkompilasi hello world tanpa bantuan fungsi C di Windows?
Jawaban:
Memanggil libc stdio printf
, mengimplementasikanint main(){ return printf(message); }
; ----------------------------------------------------------------------------
; helloworld.asm
;
; This is a Win32 console program that writes "Hello, World" on one line and
; then exits. It needs to be linked with a C library.
; ----------------------------------------------------------------------------
global _main
extern _printf
section .text
_main:
push message
call _printf
add esp, 4
ret
message:
db 'Hello, World', 10, 0
Lalu lari
nasm -fwin32 helloworld.asm
gcc helloworld.obj
a
Ada juga The Clueless Newbies Guide to Hello World di Nasm tanpa menggunakan C library. Maka kodenya akan terlihat seperti ini.
Kode 16-bit dengan panggilan sistem MS-DOS: berfungsi di emulator DOS atau di Windows 32-bit dengan dukungan NTVDM . Tidak dapat dijalankan "secara langsung" (secara transparan) pada Windows 64-bit mana pun, karena kernel x86-64 tidak dapat menggunakan mode vm86.
org 100h
mov dx,msg
mov ah,9
int 21h
mov ah,4Ch
int 21h
msg db 'Hello, World!',0Dh,0Ah,'$'
Buat ini menjadi .com
file yang dapat dieksekusi sehingga akan dimuat cs:100h
dengan semua register segmen sama satu sama lain (model memori kecil).
Semoga berhasil.
Contoh ini menunjukkan bagaimana untuk pergi langsung ke Windows API dan bukan link di C Standard Library.
global _main
extern _GetStdHandle@4
extern _WriteFile@20
extern _ExitProcess@4
section .text
_main:
; DWORD bytes;
mov ebp, esp
sub esp, 4
; hStdOut = GetstdHandle( STD_OUTPUT_HANDLE)
push -11
call _GetStdHandle@4
mov ebx, eax
; WriteFile( hstdOut, message, length(message), &bytes, 0);
push 0
lea eax, [ebp-4]
push eax
push (message_end - message)
push message
push ebx
call _WriteFile@20
; ExitProcess(0)
push 0
call _ExitProcess@4
; never here
hlt
message:
db 'Hello, World', 10
message_end:
Untuk mengkompilasi, Anda memerlukan NASM dan LINK.EXE (dari Visual studio Standard Edition)
nasm -fwin32 hello.asm link / subsistem: konsol / nodefaultlib / entri: main hello.obj
gcc hello.obj
Ini adalah contoh Win32 dan Win64 yang menggunakan panggilan API Windows. Mereka untuk MASM daripada NASM, tapi lihatlah mereka. Anda dapat menemukan detail lebih lanjut di artikel ini .
Ini menggunakan MessageBox daripada mencetak ke stdout.
;---ASM Hello World Win32 MessageBox
.386
.model flat, stdcall
include kernel32.inc
includelib kernel32.lib
include user32.inc
includelib user32.lib
.data
title db 'Win32', 0
msg db 'Hello World', 0
.code
Main:
push 0 ; uType = MB_OK
push offset title ; LPCSTR lpCaption
push offset msg ; LPCSTR lpText
push 0 ; hWnd = HWND_DESKTOP
call MessageBoxA
push eax ; uExitCode = MessageBox(...)
call ExitProcess
End Main
;---ASM Hello World Win64 MessageBox
extrn MessageBoxA: PROC
extrn ExitProcess: PROC
.data
title db 'Win64', 0
msg db 'Hello World!', 0
.code
main proc
sub rsp, 28h
mov rcx, 0 ; hWnd = HWND_DESKTOP
lea rdx, msg ; LPCSTR lpText
lea r8, title ; LPCSTR lpCaption
mov r9d, 0 ; uType = MB_OK
call MessageBoxA
add rsp, 28h
mov ecx, eax ; uExitCode = MessageBox(...)
call ExitProcess
main endp
End
Untuk merakit dan menautkan ini menggunakan MASM, gunakan ini untuk 32-bit yang dapat dieksekusi:
ml.exe [filename] /link /subsystem:windows
/defaultlib:kernel32.lib /defaultlib:user32.lib /entry:Main
atau ini untuk 64-bit yang dapat dieksekusi:
ml64.exe [filename] /link /subsystem:windows
/defaultlib:kernel32.lib /defaultlib:user32.lib /entry:main
Mengapa x64 Windows perlu mencadangkan 28 jam byte ruang tumpukan sebelum a call
? Itu berarti 32 byte (0x20) shadow space alias ruang rumah, seperti yang disyaratkan oleh konvensi pemanggil. Dan 8 byte untuk re-menyelaraskan stack dengan 16, karena konvensi pemanggilan membutuhkan RSP menjadi 16-byte selaras sebelum sebuah call
. ( main
Pemanggil kami (dalam kode startup CRT) melakukan itu. Alamat pengembalian 8-byte berarti RSP berjarak 8 byte dari batas 16-byte saat masuk ke suatu fungsi.)
Shadow space dapat digunakan oleh sebuah fungsi untuk membuang argumen registernya di sebelah tempat setiap argumen tumpukan (jika ada). A system call
membutuhkan 30 jam (48 byte) untuk juga menyediakan ruang untuk r10 dan r11 selain 4 register yang disebutkan sebelumnya. Tetapi panggilan DLL hanyalah panggilan fungsi, meskipun panggilan tersebut membungkus sekitar syscall
instruksi.
Fakta menarik: non-Windows, yaitu konvensi pemanggilan Sistem V x86-64 (misalnya di Linux) tidak menggunakan spasi bayangan sama sekali, dan menggunakan hingga 6 argumen register integer / pointer, dan hingga 8 argumen FP di register XMM .
Dengan menggunakan invoke
direktif MASM (yang mengetahui konvensi pemanggilan), Anda dapat menggunakan salah satu ifdef untuk membuat versi ini yang dapat dibangun sebagai 32-bit atau 64-bit.
ifdef rax
extrn MessageBoxA: PROC
extrn ExitProcess: PROC
else
.386
.model flat, stdcall
include kernel32.inc
includelib kernel32.lib
include user32.inc
includelib user32.lib
endif
.data
caption db 'WinAPI', 0
text db 'Hello World', 0
.code
main proc
invoke MessageBoxA, 0, offset text, offset caption, 0
invoke ExitProcess, eax
main endp
end
Varian makro sama untuk keduanya, tetapi Anda tidak akan belajar perakitan dengan cara ini. Anda akan belajar asm gaya-C sebagai gantinya. invoke
adalah untuk stdcall
atau fastcall
sementara cinvoke
untuk cdecl
atau variabel argumen fastcall
. Assembler tahu mana yang akan digunakan.
Anda dapat membongkar keluaran untuk melihat seberapa invoke
luas.
title
sebagai nama label, saya mengalami kesalahan. Namun ketika saya menggunakan sesuatu yang lain sebagai nama label mytitle
, semuanya berfungsi dengan baik.
Flat Assembler tidak membutuhkan linker tambahan. Ini membuat pemrograman assembler cukup mudah. Ini juga tersedia untuk Linux.
Ini hello.asm
dari contoh Fasm:
include 'win32ax.inc'
.code
start:
invoke MessageBox,HWND_DESKTOP,"Hi! I'm the example program!",invoke GetCommandLine,MB_OK
invoke ExitProcess,0
.end start
Fasm membuat eksekusi:
> fasm hello.asm perakit datar versi 1.70.03 (memori 1048575 kilobyte) 4 lintasan, 1536 byte.
Dan inilah program di IDA :
Anda dapat melihat tiga panggilan: GetCommandLine
, MessageBox
dan ExitProcess
.
Untuk mendapatkan .exe dengan NASM'compiler dan linker Visual Studio, kode ini berfungsi dengan baik:
global WinMain
extern ExitProcess ; external functions in system libraries
extern MessageBoxA
section .data
title: db 'Win64', 0
msg: db 'Hello world!', 0
section .text
WinMain:
sub rsp, 28h
mov rcx, 0 ; hWnd = HWND_DESKTOP
lea rdx,[msg] ; LPCSTR lpText
lea r8,[title] ; LPCSTR lpCaption
mov r9d, 0 ; uType = MB_OK
call MessageBoxA
add rsp, 28h
mov ecx,eax
call ExitProcess
hlt ; never here
Jika kode ini disimpan di misalnya "test64.asm", maka untuk mengkompilasi:
nasm -f win64 test64.asm
Menghasilkan "test64.obj" Lalu untuk menautkan dari command prompt:
path_to_link\link.exe test64.obj /subsystem:windows /entry:WinMain /libpath:path_to_libs /nodefaultlib kernel32.lib user32.lib /largeaddressaware:no
di mana path_to_link bisa jadi C: \ Program Files (x86) \ Microsoft Visual Studio 10.0 \ VC \ bin atau di mana pun program link.exe Anda di mesin Anda, path_to_libs bisa berupa C: \ Program Files (x86) \ Windows Kits \ 8.1 \ Lib \ winv6.3 \ um \ x64 atau di mana pun perpustakaan Anda (dalam hal ini kernel32.lib dan user32.lib berada di tempat yang sama, jika tidak gunakan satu opsi untuk setiap jalur yang Anda butuhkan) dan / largeaddressaware: tidak ada opsi yang diperlukan untuk menghindari keluhan linker tentang alamat ke long (untuk user32.lib dalam kasus ini). Selain itu, seperti yang dilakukan di sini, jika Visual's linker dipanggil dari command prompt, maka lingkungan perlu disiapkan sebelumnya (jalankan sekali vcvarsall.bat dan / atau lihat MS C ++ 2010 dan mspdb100.dll ).
default rel
di bagian atas file Anda sehingga mode pengalamatan tersebut ( [msg]
dan [title]
) menggunakan pengalamatan relatif RIP daripada absolut 32-bit.
Kecuali jika Anda menelepon suatu fungsi, ini sama sekali tidak sepele. (Dan, sungguh, tidak ada perbedaan nyata dalam kompleksitas antara memanggil printf dan memanggil fungsi api win32.)
Bahkan DOS int 21h benar-benar hanya sebuah pemanggilan fungsi, meskipun itu adalah API yang berbeda.
Jika Anda ingin melakukannya tanpa bantuan, Anda perlu berbicara dengan perangkat keras video Anda secara langsung, kemungkinan besar menulis bitmap dari huruf "Hello world" ke dalam framebuffer. Bahkan kemudian kartu video melakukan pekerjaan menerjemahkan nilai-nilai memori tersebut menjadi sinyal VGA / DVI.
Perhatikan bahwa, sungguh, tidak satu pun dari hal-hal ini sampai ke perangkat keras yang lebih menarik di ASM daripada di C. Program "hello world" bermuara pada pemanggilan fungsi. Satu hal yang menyenangkan tentang ASM adalah Anda dapat menggunakan ABI apa pun yang Anda inginkan dengan cukup mudah; Anda hanya perlu tahu apa itu ABI.
Contoh terbaik adalah mereka yang memiliki fasm, karena fasm tidak menggunakan linker, yang menyembunyikan kompleksitas pemrograman windows oleh lapisan kompleksitas buram lainnya. Jika Anda puas dengan program yang menulis ke jendela gui, maka ada contohnya di direktori contoh fasm.
Jika Anda menginginkan program konsol, yang memungkinkan pengalihan standar masuk dan keluar standar yang juga dimungkinkan. Ada program contoh (helas sangat non-sepele) tersedia yang tidak menggunakan gui, dan bekerja secara ketat dengan konsol, yaitu fasm itu sendiri. Ini dapat dikurangi menjadi hal-hal penting. (Saya telah menulis kompiler keempat yang merupakan contoh non-gui lainnya, tetapi juga tidak sepele).
Program semacam itu memiliki perintah berikut untuk menghasilkan header yang tepat untuk 32-bit yang dapat dieksekusi, biasanya dilakukan oleh linker.
FORMAT PE CONSOLE
Sebuah bagian bernama '.idata' berisi tabel yang membantu windows selama startup untuk beberapa nama fungsi ke alamat runtime. Ini juga berisi referensi ke KERNEL.DLL yang merupakan Sistem Operasi Windows.
section '.idata' import data readable writeable
dd 0,0,0,rva kernel_name,rva kernel_table
dd 0,0,0,0,0
kernel_table:
_ExitProcess@4 DD rva _ExitProcess
CreateFile DD rva _CreateFileA
...
...
_GetStdHandle@4 DD rva _GetStdHandle
DD 0
Format tabel diberlakukan oleh windows dan berisi nama yang dicari di file sistem, saat program dimulai. FASM menyembunyikan beberapa kerumitan di balik kata kunci rva. Jadi _ExitProcess @ 4 adalah label fasm dan _exitProcess adalah string yang dicari oleh Windows.
Program Anda ada di bagian '.text'. Jika Anda menyatakan bahwa bagian itu dapat dibaca dan dapat dieksekusi, itu adalah satu-satunya bagian yang perlu Anda tambahkan.
section '.text' code executable readable writable
Anda dapat memanggil semua fasilitas yang Anda nyatakan di bagian .idata. Untuk program konsol, Anda memerlukan _GetStdHandle untuk menemukan dia mengajukanescriptors untuk standard in dan standardout (menggunakan nama simbolik seperti STD_INPUT_HANDLE yang ditemukan fasm di file include win32a.inc). Setelah Anda memiliki deskriptor file, Anda dapat melakukan WriteFile dan ReadFile. Semua fungsi dijelaskan dalam dokumentasi kernel32. Anda mungkin menyadarinya atau Anda tidak akan mencoba pemrograman assembler.
Singkatnya: Ada tabel dengan nama asci yang berpasangan dengan OS windows. Selama permulaan ini diubah menjadi tabel alamat yang dapat dipanggil, yang Anda gunakan dalam program Anda.
Jika Anda ingin menggunakan NASM dan Visual Studio linker (link.exe) dengan contoh Hello World anderstornvig, Anda harus menautkan secara manual dengan C Runtime Libary yang berisi fungsi printf ().
nasm -fwin32 helloworld.asm
link.exe helloworld.obj libcmt.lib
Semoga ini bisa membantu seseorang.