Zobacz także zestaw startowy do montażu okien Steve Gibson's Small Is Beautiful .
Jeremy
Nieużywanie bibliotek c jest nieco dziwnym ograniczeniem. Trzeba zadzwonić do jakiejś biblioteki w systemie operacyjnym MS-Windows. prawdopodobnie kernel32.dll. To, czy Microsoft napisał to w c, czy Pascal, wydaje się nieistotne. Czy oznacza to, że można wywoływać tylko funkcje dostarczane przez system operacyjny, co w systemie typu Unix nazywa się wywołaniami systemowymi?
Albert van der Horst
Zakładam, że w przypadku bibliotek C ma on na myśli brak bibliotek wykonawczych C, takich jak te, które są dostarczane z GCC lub MSVC. Oczywiście będzie musiał użyć niektórych standardowych bibliotek DLL systemu Windows, takich jak kernel32.dll.
Rudy Velthuis
2
Różnica między kernel32.dll i biblioteką wykonawczą gcc nie jest w formacie (obie są dll) i nie w języku (oba są prawdopodobnie c, ale to jest ukryte). Różnica jest między dostarczonymi przez system operacyjny lub nie.
Albert van der Horst
Szukałem tego też lol nie mogłem znaleźć niczego z fasm bez uwzględnienia
; ----------------------------------------------------------------------------
; helloworld.asm
;
; This is a Win32 console program that writes "Hello, World"on one lineand
; 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
16-bitowy kod z wywołaniami systemowymi MS-DOS: działa w emulatorach DOS lub w 32-bitowym Windowsie z obsługą NTVDM . Nie można uruchomić „bezpośrednio” (w sposób przezroczysty) w żadnym 64-bitowym systemie Windows, ponieważ jądro x86-64 nie może używać trybu vm86.
Pytanie wyraźnie mówi „bez korzystania z bibliotek C”
Mehrdad Afshari
25
Źle. Sama biblioteka C oczywiście może, więc jest to możliwe. W rzeczywistości jest to tylko trochę trudniejsze. Wystarczy wywołać WriteConsole () z 5 odpowiednimi parametrami.
MSalters
12
Chociaż drugi przykład nie wywołuje żadnej funkcji biblioteki C, to również nie jest programem Windows. Wirtualna maszyna DOS zostanie uruchomiona, aby ją uruchomić.
Rômulo Ceccon
7
@Alex Hart, jego drugi przykład dotyczy systemu DOS, a nie systemu Windows. W DOS, programy w małym trybie (pliki .COM, poniżej 64Kb całkowitego kodu + dane + stos) zaczynają się od 0x100h, ponieważ pierwsze 256 bajtów w segmencie jest pobierane przez PSP (argumenty wiersza poleceń itp.). Zobacz ten link: en.wikipedia.org/wiki/Program_Segment_Prefix
zvolkov
7
Nie o to proszono. Pierwszy przykład wykorzystuje bibliotekę C, a drugi to MS-DOS, a nie Windows.
Paulo Pinto,
131
Ten przykład pokazuje, jak przejść bezpośrednio do interfejsu API systemu Windows i nie łączyć w bibliotece standardowej języka C.
prawdopodobnie będziesz musiał dołączyć kernel32.lib, aby to połączyć (tak zrobiłem). link / subsystem: console / nodefaultlib / entry: main hello.obj kernel32.lib
Oto przykłady Win32 i Win64 wykorzystujące wywołania Windows API. Są dla MASM, a nie NASM, ale spójrz na nie. Więcej szczegółów znajdziesz w tym artykule.
Używa MessageBox zamiast drukowania na standardowe wyjście.
Win32 MASM
;---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:
push0 ; uType = MB_OK
push offset title ; LPCSTR lpCaption
push offset msg ; LPCSTR lpText
push0 ; hWnd = HWND_DESKTOP
call MessageBoxA
push eax ; uExitCode = MessageBox(...)
call ExitProcess
End Main
Win64 MASM
;---ASM Hello World Win64 MessageBox
extrn MessageBoxA: PROC
extrn ExitProcess: PROC
.data
title db 'Win64', 0
msg db 'Hello World!', 0
.code
main proc
subrsp, 28hmovrcx, 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
Aby złożyć i połączyć je za pomocą MASM, użyj tego dla 32-bitowego pliku wykonywalnego:
Dlaczego system Windows x64 musi rezerwować 28-godzinne bajty miejsca na stosie przed call? To 32 bajty (0x20) przestrzeni w cieniu, czyli przestrzeni domowej, zgodnie z wymogami konwencji wywoływania. A kolejne 8 bajtów, aby ponownie wyrównać stos o 16, ponieważ konwencja wywoływania wymaga RSP być 16-bajtowy wyrównany przedcall . ( mainZrobił to nasz rozmówca (w kodzie startowym CRT). 8-bajtowy adres zwrotny oznacza, że RSP jest oddalone o 8 bajtów od 16-bajtowej granicy wejścia do funkcji.)
Przestrzeń cienia może być używana przez funkcję do zrzucenia swoich rejestrowych argumentów obok miejsca, w którym byłyby dowolne argumenty stosu (jeśli takie istnieją). A system callwymaga 30h (48 bajtów), aby również zarezerwować miejsce dla r10 i r11 oprócz wcześniej wspomnianych 4 rejestrów. Ale wywołania DLL to tylko wywołania funkcji, nawet jeśli są opakowaniami wokół syscallinstrukcji.
Ciekawostka: inne niż Windows, tj. Konwencja wywoływania x86-64 System V (np. W Linuksie) w ogóle nie wykorzystuje przestrzeni w cieniu i używa do 6 argumentów rejestrów liczb całkowitych / wskaźników i do 8 argumentów FP w rejestrach XMM .
Używając invokedyrektywy MASM (która zna konwencję wywoływania), możesz użyć jednego ifdef, aby stworzyć wersję tego, która może być zbudowana jako 32-bitowa lub 64-bitowa.
Wariant makro jest taki sam dla obu, ale w ten sposób nie nauczysz się asemblacji. Zamiast tego nauczysz się asm w stylu C. invokejest for stdcalllub fastcallwhile cinvokejest cdeclargumentem for lub zmiennym fastcall. Asembler wie, którego użyć.
Możesz zdemontować wyjście, aby zobaczyć, jak invokerozszerzone.
+1 za twoją odpowiedź. Czy możesz dodać kod montażowy dla systemu Windows na ARM (WOA)?
Annie,
1
Dlaczego rsp wymaga 0x28 bajtów, a nie 0x20? Wszystkie odniesienia do konwencji wywoływania mówią, że powinno to być 32, ale wydaje się, że w praktyce wymaga to 40.
douggard
W twoim 32-bitowym kodzie skrzynki wiadomości z jakiegoś powodu, gdy używam titlenazwy etykiety, napotykam błędy. Jednak kiedy używam czegoś innego jako nazwy etykiety mytitle, wszystko działa dobrze.
user3405291
jak to zrobić bez zoiut zawiera?
bluejayke
14
Flat Assembler nie potrzebuje dodatkowego linkera. To sprawia, że programowanie w asemblerze jest dość łatwe. Jest również dostępny dla systemu Linux.
To pochodzi hello.asmz przykładów 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 tworzy plik wykonywalny:
> fasm hello.asm
flat assembler w wersji 1.70.03 (pamięć 1048575 kilobajtów)
4 przebiegi, 1536 bajtów.
gdzie ścieżka_do_łącza może być C: \ Program Files (x86) \ Microsoft Visual Studio 10.0 \ VC \ bin lub gdziekolwiek jest twój program link.exe na twoim komputerze,
ścieżka_do_libs może być C: \ Program Files (x86) \ Windows Kits \ 8.1 \ Lib \ winv6.3 \ um \ x64 lub gdziekolwiek są twoje biblioteki (w tym przypadku zarówno kernel32.lib, jak i user32.lib znajdują się w tym samym miejscu, w przeciwnym razie użyj jednej opcji dla każdej ścieżki, której potrzebujesz) i / largeaddressaware: żadna opcja nie jest konieczne, aby uniknąć narzekania linkera na adresy zbyt długie (w tym przypadku dla user32.lib). Podobnie jak w tym przypadku, jeśli linker Visual jest wywoływany z wiersza poleceń, konieczne jest wcześniejsze skonfigurowanie środowiska (uruchom raz vcvarsall.bat i / lub zobacz MS C ++ 2010 i mspdb100.dll).
Zdecydowanie zalecam używanie default relna początku pliku, więc te tryby adresowania ( [msg]i [title]) używają adresowania względnego RIP zamiast 32-bitowego bezwzględnego.
Peter Cordes
Dziękuję za wyjaśnienie, jak utworzyć link! Uratowałeś moje zdrowie psychiczne. Zacząłem wyrywać sobie włosy z „błędu LNK2001: nierozwiązany zewnętrzny symbol ExitProcess” i podobne błędy ...
Nik
5
O ile nie wywołasz jakiejś funkcji, nie jest to wcale trywialne. (I poważnie, nie ma prawdziwej różnicy w złożoności między wywołaniem printf a wywołaniem funkcji api win32).
Nawet DOS int 21h jest tak naprawdę tylko wywołaniem funkcji, nawet jeśli jest to inny interfejs API.
Jeśli chcesz to zrobić bez pomocy, musisz porozmawiać bezpośrednio ze sprzętem wideo, prawdopodobnie zapisując bitmapy liter „Hello world” w buforze ramki. Nawet wtedy karta graficzna przetwarza te wartości pamięci na sygnały VGA / DVI.
Zauważ, że tak naprawdę żadna z tych rzeczy, aż do sprzętu, nie jest bardziej interesująca w ASM niż w C. Program „hello world” sprowadza się do wywołania funkcji. Jedną z fajnych rzeczy w ASM jest to, że możesz dość łatwo używać dowolnego ABI; musisz tylko wiedzieć, co to jest ABI.
To doskonała uwaga - zarówno ASM, jak i C opierają się na funkcji dostarczonej przez system operacyjny (_WriteFile w systemie Windows). Więc gdzie jest magia? Znajduje się w kodzie sterownika urządzenia dla karty graficznej.
Assad Ebrahim
2
To zupełnie nie ma znaczenia. Plakat prosi program asemblera działający „pod Windows”. Oznacza to, że można używać narzędzi systemu Windows (np. Kernel32.dll), ale nie innych udogodnień, takich jak libc w Cygwin. Do głośnego płaczu, plakat wyraźnie mówi, że nie ma bibliotek c.
Albert van der Horst
5
Najlepszymi przykładami są te z fasm, ponieważ fasm nie używa linkera, który ukrywa złożoność programowania systemu Windows za inną nieprzezroczystą warstwą złożoności. Jeśli jesteś zadowolony z programu, który pisze w oknie GUI, to jest na to przykład w przykładowym katalogu fasm.
Jeśli potrzebujesz programu konsoli, który umożliwia przekierowanie standardowego wejścia i wyjścia, to jest to również możliwe. Jest dostępny (hej, bardzo nietrywialny) przykładowy program, który nie używa interfejsu GUI i działa ściśle z konsolą, czyli samą fasm. Można to przerzucić na podstawowe. (Napisałem czwarty kompilator, który jest kolejnym przykładem non-gui, ale jest również nietrywialny).
Taki program ma następujące polecenie do generowania odpowiedniego nagłówka dla 32-bitowego pliku wykonywalnego, zwykle wykonywane przez konsolidator.
FORMAT PE CONSOLE
Sekcja o nazwie „.idata” zawiera tabelę, która pomaga oknom podczas uruchamiania łączyć nazwy funkcji z adresami środowiska wykonawczego. Zawiera również odniesienie do KERNEL.DLL, który jest systemem operacyjnym Windows.
Format tabeli jest narzucany przez okna i zawiera nazwy, które są wyszukiwane w plikach systemowych podczas uruchamiania programu. FASM ukrywa część złożoności słowa kluczowego rva. Zatem _ExitProcess @ 4 to etykieta fasm, a _exitProcess to ciąg znaków, który jest wyszukiwany przez system Windows.
Twój program znajduje się w sekcji „.text”. Jeśli zadeklarujesz, że sekcja jest czytelna, zapisywalna i wykonywalna, jest to jedyna sekcja, którą musisz dodać.
section '.text' code executable readable writable
Możesz zadzwonić do wszystkich obiektów zadeklarowanych w sekcji .idata. W przypadku programu konsoli potrzebujesz _GetStdHandle, aby znaleźć deskryptory plików dla standardu in i standardout (używając nazw symbolicznych, takich jak STD_INPUT_HANDLE, które fasm znajduje w pliku dołączanym win32a.inc). Gdy masz już deskryptory plików, możesz wykonać WriteFile i ReadFile. Wszystkie funkcje są opisane w dokumentacji kernel32. Prawdopodobnie jesteś tego świadomy lub nie próbowałbyś programować w asemblerze.
Podsumowując: istnieje tabela z nazwami ASCI, które są powiązane z systemem operacyjnym Windows. Podczas uruchamiania jest to przekształcane w tabelę adresów możliwych do wywołania, których używasz w swoim programie.
FASM może nie używać konsolidatora, ale nadal musi złożyć plik PE. Co oznacza, że w rzeczywistości nie tylko asembluje kod, ale także bierze na siebie pracę, którą normalnie wykonywałby linker, i jako taka, moim skromnym zdaniem, mylące jest nazywanie braku linkera „ukrywającego złożoność”, wręcz przeciwnie - zadaniem asemblera jest asemblowanie programu, ale pozostawienie linkerowi osadzenia programu w obrazie programu, co może zależeć od wielu rzeczy. W związku z tym uważam, że oddzielenie konsolidatora od asemblera jest dobrą rzeczą, z którą, jak się wydaje, się nie zgadzasz.
rano
@amn Pomyśl o tym w ten sposób. Jeśli używasz linkera do tworzenia powyższego programu, czy daje ci on więcej wglądu w to, co robi program lub z czego się składa? Jeśli spojrzę na źródło fasm, znam pełną strukturę programu.
Albert van der Horst
Słuszna uwaga. Z drugiej strony oddzielenie linkowania od wszystkiego innego ma również swoje zalety. Zwykle masz dostęp do pliku obiektowego (co znacznie ułatwia inspekcję struktury programu, niezależnie od formatu pliku obrazu programu), możesz wywołać inny konsolidator według własnych preferencji, z różnymi opcjami. Chodzi o możliwość ponownego użycia i komponowania. Mając to na uwadze, FASM robi wszystko, ponieważ jest „wygodny”, łamie te zasady. Zasadniczo nie jestem temu przeciwny - widzę ich uzasadnienie - ale po pierwsze, nie potrzebuję tego.
AMN
uzyskać błąd za nielegalne włamanie w górnej linii w 64-bitowych oknach
fasm
@bluejayke Prawdopodobnie nie miałeś pod ręką dokumentacji do fasm. FORMAT PE generuje 32-bitowy plik wykonywalny, którego 64-bitowe okna odmawiają uruchomienia. Dla programu 64-bitowego chcesz FORMAT PE64. Upewnij się również, że używasz w swoim programie odpowiednich instrukcji 64-bitowych.
Albert van der Horst
3
Jeśli chcesz używać NASM i linkera Visual Studio (link.exe) z przykładem Hello World anderstornviga, będziesz musiał ręcznie połączyć się z biblioteką C Runtime Libary, która zawiera funkcję printf ().
Odpowiedzi:
Przykłady NASM .
Wywołanie libc stdio
printf
, implementacjaint 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
Następnie uruchomić
nasm -fwin32 helloworld.asm gcc helloworld.obj a
Istnieje również The Clueless Newbies Guide to Hello World w Nasm bez użycia biblioteki C. Wtedy kod wyglądałby tak.
16-bitowy kod z wywołaniami systemowymi MS-DOS: działa w emulatorach DOS lub w 32-bitowym Windowsie z obsługą NTVDM . Nie można uruchomić „bezpośrednio” (w sposób przezroczysty) w żadnym 64-bitowym systemie Windows, ponieważ jądro x86-64 nie może używać trybu vm86.
org 100h mov dx,msg mov ah,9 int 21h mov ah,4Ch int 21h msg db 'Hello, World!',0Dh,0Ah,'$'
Zbuduj to w
.com
pliku wykonywalnym, aby został załadowanycs:100h
ze wszystkimi rejestrami segmentów równymi sobie (model małej pamięci).Powodzenia.
źródło
Ten przykład pokazuje, jak przejść bezpośrednio do interfejsu API systemu Windows i nie łączyć w bibliotece standardowej języka C.
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:
Aby skompilować, potrzebujesz NASM i LINK.EXE (z Visual studio Standard Edition)
źródło
gcc hello.obj
Oto przykłady Win32 i Win64 wykorzystujące wywołania Windows API. Są dla MASM, a nie NASM, ale spójrz na nie. Więcej szczegółów znajdziesz w tym artykule.
Używa MessageBox zamiast drukowania na standardowe wyjście.
Win32 MASM
;---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
Win64 MASM
;---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
Aby złożyć i połączyć je za pomocą MASM, użyj tego dla 32-bitowego pliku wykonywalnego:
ml.exe [filename] /link /subsystem:windows /defaultlib:kernel32.lib /defaultlib:user32.lib /entry:Main
lub to dla 64-bitowego pliku wykonywalnego:
ml64.exe [filename] /link /subsystem:windows /defaultlib:kernel32.lib /defaultlib:user32.lib /entry:main
Dlaczego system Windows x64 musi rezerwować 28-godzinne bajty miejsca na stosie przed
call
? To 32 bajty (0x20) przestrzeni w cieniu, czyli przestrzeni domowej, zgodnie z wymogami konwencji wywoływania. A kolejne 8 bajtów, aby ponownie wyrównać stos o 16, ponieważ konwencja wywoływania wymaga RSP być 16-bajtowy wyrównany przedcall
. (main
Zrobił to nasz rozmówca (w kodzie startowym CRT). 8-bajtowy adres zwrotny oznacza, że RSP jest oddalone o 8 bajtów od 16-bajtowej granicy wejścia do funkcji.)Przestrzeń cienia może być używana przez funkcję do zrzucenia swoich rejestrowych argumentów obok miejsca, w którym byłyby dowolne argumenty stosu (jeśli takie istnieją). A
system call
wymaga 30h (48 bajtów), aby również zarezerwować miejsce dla r10 i r11 oprócz wcześniej wspomnianych 4 rejestrów. Ale wywołania DLL to tylko wywołania funkcji, nawet jeśli są opakowaniami wokółsyscall
instrukcji.Ciekawostka: inne niż Windows, tj. Konwencja wywoływania x86-64 System V (np. W Linuksie) w ogóle nie wykorzystuje przestrzeni w cieniu i używa do 6 argumentów rejestrów liczb całkowitych / wskaźników i do 8 argumentów FP w rejestrach XMM .
Używając
invoke
dyrektywy MASM (która zna konwencję wywoływania), możesz użyć jednego ifdef, aby stworzyć wersję tego, która może być zbudowana jako 32-bitowa lub 64-bitowa.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
Wariant makro jest taki sam dla obu, ale w ten sposób nie nauczysz się asemblacji. Zamiast tego nauczysz się asm w stylu C.
invoke
jest forstdcall
lubfastcall
whilecinvoke
jestcdecl
argumentem for lub zmiennymfastcall
. Asembler wie, którego użyć.Możesz zdemontować wyjście, aby zobaczyć, jak
invoke
rozszerzone.źródło
title
nazwy etykiety, napotykam błędy. Jednak kiedy używam czegoś innego jako nazwy etykietymytitle
, wszystko działa dobrze.Flat Assembler nie potrzebuje dodatkowego linkera. To sprawia, że programowanie w asemblerze jest dość łatwe. Jest również dostępny dla systemu Linux.
To pochodzi
hello.asm
z przykładów 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 tworzy plik wykonywalny:
A oto program w IDA :
Można zobaczyć trzy połączenia:
GetCommandLine
,MessageBox
iExitProcess
.źródło
Aby uzyskać plik .exe z kompilatorem NASM i konsolidatorem programu Visual Studio, ten kod działa dobrze:
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
Jeśli ten kod jest zapisany np. Na „test64.asm”, to do kompilacji:
nasm -f win64 test64.asm
Tworzy plik „test64.obj”. Następnie, aby połączyć z wiersza polecenia:
path_to_link\link.exe test64.obj /subsystem:windows /entry:WinMain /libpath:path_to_libs /nodefaultlib kernel32.lib user32.lib /largeaddressaware:no
gdzie ścieżka_do_łącza może być C: \ Program Files (x86) \ Microsoft Visual Studio 10.0 \ VC \ bin lub gdziekolwiek jest twój program link.exe na twoim komputerze, ścieżka_do_libs może być C: \ Program Files (x86) \ Windows Kits \ 8.1 \ Lib \ winv6.3 \ um \ x64 lub gdziekolwiek są twoje biblioteki (w tym przypadku zarówno kernel32.lib, jak i user32.lib znajdują się w tym samym miejscu, w przeciwnym razie użyj jednej opcji dla każdej ścieżki, której potrzebujesz) i / largeaddressaware: żadna opcja nie jest konieczne, aby uniknąć narzekania linkera na adresy zbyt długie (w tym przypadku dla user32.lib). Podobnie jak w tym przypadku, jeśli linker Visual jest wywoływany z wiersza poleceń, konieczne jest wcześniejsze skonfigurowanie środowiska (uruchom raz vcvarsall.bat i / lub zobacz MS C ++ 2010 i mspdb100.dll).
źródło
default rel
na początku pliku, więc te tryby adresowania ([msg]
i[title]
) używają adresowania względnego RIP zamiast 32-bitowego bezwzględnego.O ile nie wywołasz jakiejś funkcji, nie jest to wcale trywialne. (I poważnie, nie ma prawdziwej różnicy w złożoności między wywołaniem printf a wywołaniem funkcji api win32).
Nawet DOS int 21h jest tak naprawdę tylko wywołaniem funkcji, nawet jeśli jest to inny interfejs API.
Jeśli chcesz to zrobić bez pomocy, musisz porozmawiać bezpośrednio ze sprzętem wideo, prawdopodobnie zapisując bitmapy liter „Hello world” w buforze ramki. Nawet wtedy karta graficzna przetwarza te wartości pamięci na sygnały VGA / DVI.
Zauważ, że tak naprawdę żadna z tych rzeczy, aż do sprzętu, nie jest bardziej interesująca w ASM niż w C. Program „hello world” sprowadza się do wywołania funkcji. Jedną z fajnych rzeczy w ASM jest to, że możesz dość łatwo używać dowolnego ABI; musisz tylko wiedzieć, co to jest ABI.
źródło
Najlepszymi przykładami są te z fasm, ponieważ fasm nie używa linkera, który ukrywa złożoność programowania systemu Windows za inną nieprzezroczystą warstwą złożoności. Jeśli jesteś zadowolony z programu, który pisze w oknie GUI, to jest na to przykład w przykładowym katalogu fasm.
Jeśli potrzebujesz programu konsoli, który umożliwia przekierowanie standardowego wejścia i wyjścia, to jest to również możliwe. Jest dostępny (hej, bardzo nietrywialny) przykładowy program, który nie używa interfejsu GUI i działa ściśle z konsolą, czyli samą fasm. Można to przerzucić na podstawowe. (Napisałem czwarty kompilator, który jest kolejnym przykładem non-gui, ale jest również nietrywialny).
Taki program ma następujące polecenie do generowania odpowiedniego nagłówka dla 32-bitowego pliku wykonywalnego, zwykle wykonywane przez konsolidator.
Sekcja o nazwie „.idata” zawiera tabelę, która pomaga oknom podczas uruchamiania łączyć nazwy funkcji z adresami środowiska wykonawczego. Zawiera również odniesienie do KERNEL.DLL, który jest systemem operacyjnym 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 tabeli jest narzucany przez okna i zawiera nazwy, które są wyszukiwane w plikach systemowych podczas uruchamiania programu. FASM ukrywa część złożoności słowa kluczowego rva. Zatem _ExitProcess @ 4 to etykieta fasm, a _exitProcess to ciąg znaków, który jest wyszukiwany przez system Windows.
Twój program znajduje się w sekcji „.text”. Jeśli zadeklarujesz, że sekcja jest czytelna, zapisywalna i wykonywalna, jest to jedyna sekcja, którą musisz dodać.
section '.text' code executable readable writable
Możesz zadzwonić do wszystkich obiektów zadeklarowanych w sekcji .idata. W przypadku programu konsoli potrzebujesz _GetStdHandle, aby znaleźć deskryptory plików dla standardu in i standardout (używając nazw symbolicznych, takich jak STD_INPUT_HANDLE, które fasm znajduje w pliku dołączanym win32a.inc). Gdy masz już deskryptory plików, możesz wykonać WriteFile i ReadFile. Wszystkie funkcje są opisane w dokumentacji kernel32. Prawdopodobnie jesteś tego świadomy lub nie próbowałbyś programować w asemblerze.
Podsumowując: istnieje tabela z nazwami ASCI, które są powiązane z systemem operacyjnym Windows. Podczas uruchamiania jest to przekształcane w tabelę adresów możliwych do wywołania, których używasz w swoim programie.
źródło
Jeśli chcesz używać NASM i linkera Visual Studio (link.exe) z przykładem Hello World anderstornviga, będziesz musiał ręcznie połączyć się z biblioteką C Runtime Libary, która zawiera funkcję printf ().
nasm -fwin32 helloworld.asm link.exe helloworld.obj libcmt.lib
Mam nadzieję, że to komuś pomoże.
źródło