Consultez également le kit de démarrage de l'assemblage de fenêtres Small Is Beautiful de Steve Gibson .
Jeremy
Ne pas utiliser de c-bibliothèques est une contrainte quelque peu étrange. Il faut appeler une bibliothèque dans le système d'exploitation MS-Windows. probablement kernel32.dll. Que Microsoft ait écrit cela en c ou en Pascal ne semble pas pertinent. Cela signifie-t-il que seules les fonctions fournies par le système d'exploitation peuvent être appelées, qu'est-ce que dans un système de type Unix serait appelé appels système?
Albert van der Horst
Avec les bibliothèques C, je suppose qu'il ou elle veut dire sans utiliser de bibliothèques d'exécution C comme celles fournies avec GCC ou MSVC. Bien sûr, il devra utiliser des DLL Windows standard, comme kernel32.dll.
Rudy Velthuis
2
La distinction entre kernel32.dll et une bibliothèque d'exécution gcc n'est pas dans le format (les deux sont dll) et pas dans le langage (les deux sont probablement c, mais cela est caché.) La différence est entre le système d'exploitation ou non.
Albert van der Horst
J'étais à la recherche de cela aussi lol ne pouvait rien trouver avec fasm sans inclut
Appel de libc stdio printf, implémentationint main(){ return printf(message); }
;----------------------------------------------------------------------------; helloworld.asm;;Thisis 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
Code 16 bits avec appels système MS-DOS: fonctionne dans les émulateurs DOS ou dans Windows 32 bits avec prise en charge NTVDM . Ne peut pas être exécuté "directement" (de manière transparente) sous n'importe quel Windows 64 bits, car un noyau x86-64 ne peut pas utiliser le mode vm86.
Construisez-le dans un .comexécutable pour qu'il soit chargé cs:100havec tous les registres de segment égaux les uns aux autres (modèle de mémoire minuscule).
La question mentionne explicitement "sans utiliser les bibliothèques C"
Mehrdad Afshari
25
Faux. La bibliothèque C elle-même le peut évidemment, c'est donc possible. Ce n'est qu'un peu plus difficile, en fait. Il vous suffit d'appeler WriteConsole () avec les 5 bons paramètres.
MSalters
12
Bien que le deuxième exemple n'appelle aucune fonction de bibliothèque C, ce n'est pas non plus un programme Windows. La machine DOS virtuelle sera déclenchée pour l'exécuter.
Rômulo Ceccon
7
@Alex Hart, son deuxième exemple est pour DOS, pas pour Windows. Sous DOS, les programmes en mode minuscule (fichiers .COM, sous 64Kb de code total + données + pile) démarrent à 0x100h car les 256 premiers octets du segment sont pris par la PSP (args de ligne de commande, etc.). Voir ce lien: en.wikipedia.org/wiki/Program_Segment_Prefix
zvolkov
7
Ce n'est pas ce qui a été demandé. Le premier exemple utilise la bibliothèque C et le second est MS-DOS, pas Windows.
Paulo Pinto
128
Cet exemple montre comment accéder directement à l'API Windows sans créer de lien dans la bibliothèque C Standard.
vous devrez probablement inclure le kernel32.lib pour lier ceci (je l'ai fait). lien / sous-système: console / nodefaultlib / entrée: main hello.obj kernel32.lib
Zach Burlingame
5
Comment lier l'obj avec ld.exe depuis MinGW?
DarrenVortex
4
@DarrenVortexgcc hello.obj
Towry
4
Cela fonctionnerait-il également en utilisant des liens gratuits comme Alink de sourceforge.net/projects/alink ou GoLink de godevtool.com/#linker ? Je ne veux pas installer Visual Studio uniquement pour ça?
jj_
21
Ce sont des exemples Win32 et Win64 utilisant des appels d'API Windows. Ils sont pour MASM plutôt que NASM, mais regardez-les. Vous pouvez trouver plus de détails dans cet article.
Cela utilise MessageBox au lieu d'imprimer sur stdout.
MASM Win32
;---ASM HelloWorldWin32MessageBox.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 ExitProcessEndMain
MASM Win64
;---ASM HelloWorldWin64MessageBox
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 MessageBoxAadd rsp,28h
mov ecx, eax ; uExitCode =MessageBox(...)
call ExitProcess
main endp
End
Pour les assembler et les lier à l'aide de MASM, utilisez ceci pour un exécutable 32 bits:
Pourquoi Windows x64 doit-il réserver 28h octets d'espace de pile avant un call? Cela représente 32 octets (0x20) d'espace fantôme, c'est-à-dire l'espace d'accueil, comme l'exige la convention d'appel. Et 8 octets supplémentaires pour réaligner la pile de 16, car la convention d'appel exige que RSP soit aligné sur 16 octets avant un call. ( mainL'appelant de notre (dans le code de démarrage CRT) l'a fait. L'adresse de retour de 8 octets signifie que RSP est à 8 octets d'une limite de 16 octets à l'entrée d'une fonction.)
L'espace fantôme peut être utilisé par une fonction pour vider ses arguments de registre à côté de l'endroit où se trouveraient les arguments de la pile (le cas échéant). A system callnécessite 30h (48 octets) pour réserver également de l'espace pour r10 et r11 en plus des 4 registres mentionnés précédemment. Mais les appels DLL ne sont que des appels de fonction, même s'ils enveloppent les syscallinstructions.
Fait amusant: non-Windows, c'est-à-dire que la convention d'appel x86-64 System V (par exemple sous Linux) n'utilise pas du tout d'espace fantôme, et utilise jusqu'à 6 arguments de registre entier / pointeur, et jusqu'à 8 arguments FP dans les registres XMM .
En utilisant la invokedirective MASM (qui connaît la convention d'appel), vous pouvez utiliser un ifdef pour en faire une version qui peut être construite en 32 bits ou 64 bits.
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
La variante de macro est la même pour les deux, mais vous n'apprendrez pas l'assemblage de cette façon. Vous apprendrez plutôt l'asm de style C. invokeest pour stdcallou fastcallwhile cinvokeest pour cdeclou argument variable fastcall. L'assembleur sait lequel utiliser.
Vous pouvez désassembler la sortie pour voir son invokeextension.
+1 pour votre réponse. Pouvez-vous également ajouter un code d'assemblage pour Windows sur ARM (WOA)?
Annie
1
Pourquoi rsp nécessite-t-il 0x28 octets et non 0x20? Toutes les références sur la convention d'appel indiquent qu'il devrait être 32 mais il semble en exiger 40 en pratique.
douggard
Dans votre code de boîte de message 32 bits, pour une raison quelconque lorsque j'utilise titlecomme nom d'étiquette, je rencontre des erreurs. Cependant, lorsque j'utilise autre chose comme nom d'étiquette mytitle, tout fonctionne bien.
user3405291
comment le faire sans inclure?
bluejayke
13
Flat Assembler n'a pas besoin d'un linker supplémentaire. Cela rend la programmation de l'assembleur assez facile. Il est également disponible pour Linux.
Ceci est hello.asmtiré des exemples 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
où path_to_link pourrait être C: \ Program Files (x86) \ Microsoft Visual Studio 10.0 \ VC \ bin ou où se trouve votre programme link.exe sur votre ordinateur ,
path_to_libs pourrait être C: \ Program Files (x86) \ Windows Kits \ 8.1 \ Lib \ winv6.3 \ um \ x64 ou où se trouvent vos bibliothèques (dans ce cas, kernel32.lib et user32.lib sont au même endroit, sinon utilisez une option pour chaque chemin dont vous avez besoin) et / largeaddressaware: aucune option n'est nécessaire pour éviter de se plaindre des adresses trop longues (pour user32.lib dans ce cas). De plus, comme cela est fait ici, si l'éditeur de liens de Visual est appelé à partir de l'invite de commande, il est nécessaire de configurer l'environnement précédemment (exécutez une fois vcvarsall.bat et / ou voir MS C ++ 2010 et mspdb100.dll).
Je recommande fortement d'utiliser default relen haut de votre fichier afin que ces modes d'adressage ( [msg]et [title]) utilisent l'adressage relatif RIP au lieu de 32 bits absolu.
Peter Cordes
5
À moins que vous n'appeliez une fonction, ce n'est pas du tout trivial. (Et, sérieusement, il n'y a pas de réelle différence de complexité entre l'appel de printf et l'appel d'une fonction api win32.)
Même DOS int 21h n'est en réalité qu'un appel de fonction, même s'il s'agit d'une API différente.
Si vous voulez le faire sans aide, vous devez parler directement à votre matériel vidéo, probablement en écrivant des bitmaps des lettres "Hello world" dans un framebuffer. Même alors, la carte vidéo effectue le travail de traduction de ces valeurs de mémoire en signaux VGA / DVI.
Notez qu'en réalité, rien de tout cela jusqu'au matériel n'est plus intéressant en ASM qu'en C. Un programme "hello world" se résume à un appel de fonction. Une bonne chose à propos de l'ASM est que vous pouvez utiliser n'importe quel ABI que vous voulez assez facilement; vous avez juste besoin de savoir ce qu'est cet ABI.
C'est un excellent point --- ASM et C reposent tous deux sur une fonction fournie par le système d'exploitation (_WriteFile sous Windows). Alors, où est la magie? Il se trouve dans le code du pilote de périphérique de la carte vidéo.
Assad Ebrahim
2
C'est tout à fait autre chose. L'affiche demande un programme assembleur qui s'exécute «sous Windows». Cela signifie que les fonctionnalités de Windows peuvent être utilisées (par exemple kernel32.dll), mais pas d'autres fonctionnalités comme la libc sous Cygwin. Pour crier à haute voix, l'affiche dit explicitement qu'il n'y a pas de c-bibliothèques.
Albert van der Horst
5
Les meilleurs exemples sont ceux avec fasm, car fasm n'utilise pas d'éditeur de liens, ce qui cache la complexité de la programmation Windows par une autre couche opaque de complexité. Si vous êtes satisfait d'un programme qui écrit dans une fenêtre d'interface graphique, alors il y a un exemple pour cela dans le répertoire d'exemples de fasm.
Si vous voulez un programme console, qui permet la redirection de l'entrée standard et de la sortie standard, cela est également possible. Il existe un programme d'exemple (hélas très non trivial) disponible qui n'utilise pas d'interface graphique et qui fonctionne strictement avec la console, c'est-à-dire le fasm lui-même. Cela peut être réduit à l'essentiel. (J'ai écrit un quatrième compilateur qui est un autre exemple non-gui, mais ce n'est pas non plus trivial).
Un tel programme a la commande suivante pour générer un en-tête exécutable approprié, normalement effectué par un éditeur de liens.
FORMAT PE CONSOLE
Une section appelée «.idata» contient une table qui aide les fenêtres au démarrage à coupler les noms de fonctions aux adresses d'exécution. Il contient également une référence à KERNEL.DLL qui est le système d'exploitation Windows.
Le format de table est imposé par les fenêtres et contient des noms qui sont recherchés dans les fichiers système, au démarrage du programme. FASM cache une partie de la complexité derrière le mot-clé rva. Ainsi, _ExitProcess @ 4 est une étiquette fasm et _exitProcess est une chaîne recherchée par Windows.
Votre programme est dans la section '.text'. Si vous déclarez que cette section est accessible en écriture et exécutable, c'est la seule section que vous devez ajouter.
section '.text' code executable readable writable
Vous pouvez appeler toutes les installations que vous avez déclarées dans la section .idata. Pour un programme console, vous avez besoin de _GetStdHandle pour trouver les descripteurs de fichiers pour les entrées standard et standardout (en utilisant des noms symboliques tels que STD_INPUT_HANDLE que fasm trouve dans le fichier include win32a.inc). Une fois que vous avez les descripteurs de fichiers, vous pouvez faire WriteFile et ReadFile. Toutes les fonctions sont décrites dans la documentation de kernel32. Vous en êtes probablement conscient ou vous n'essaieriez pas de programmer l'assembleur.
En résumé: il existe un tableau avec des noms asci qui correspondent au système d'exploitation Windows. Au démarrage, cela se transforme en une table d'adresses appelables, que vous utilisez dans votre programme.
FASM peut ne pas utiliser d'éditeur de liens mais il doit encore assembler un fichier PE. Ce qui signifie qu'en fait, il ne se contente pas d'assembler du code, mais qu'il assume également un travail normalement effectué par un éditeur de liens, et en tant que tel, il est, à mon humble avis, trompeur d'appeler l'absence d'un éditeur de liens "cacher la complexité", bien au contraire - le travail d'un assembleur est d'assembler un programme, mais laissez à l'éditeur de liens le soin d'incorporer le programme dans une image de programme qui peut dépendre de beaucoup de choses. En tant que tel, je trouve que la séparation entre un lieur et un assembleur est une bonne chose, sur laquelle il semble que vous n'êtes pas d'accord.
amn
@amn Pensez-y de cette façon. Si vous utilisez un éditeur de liens pour créer le programme ci-dessus, cela vous donne-t-il plus d'informations sur ce que fait le programme ou en quoi il consiste? Si je regarde la source du fasm, je connais la structure complète du programme.
Albert van der Horst le
Bon point. D'un autre côté, séparer les liens de tout le reste a également ses avantages. Vous avez normalement accès à un fichier objet (ce qui permet également d'inspecter la structure d'un programme, indépendamment du format de fichier image du programme), vous pouvez invoquer un éditeur de liens différent de votre préférence, avec différentes options. Il s'agit de réutilisabilité et de composabilité. Dans cet esprit, FASM fait tout parce que c'est «pratique» enfreint ces principes. Je ne suis pas principalement contre - je vois leur justification - mais pour ma part, je n'en ai pas besoin.
amn le
obtenir une erreur pour une isntruction illégale sur la ligne supérieure dans les fenêtres fasm 64 bits
bluejayke
3
Si vous souhaitez utiliser NASM et l'éditeur de liens de Visual Studio (link.exe) avec l'exemple Hello World d'anderstornvig, vous devrez créer un lien manuel avec la bibliothèque C Runtime qui contient la fonction printf ().
Réponses:
Exemples de MSNA .
Appel de libc stdio
printf
, implémentationint main(){ return printf(message); }
Puis cours
Il existe également le guide des débutants Clueless pour Hello World dans Nasm sans l'utilisation d'une bibliothèque C. Ensuite, le code ressemblerait à ceci.
Code 16 bits avec appels système MS-DOS: fonctionne dans les émulateurs DOS ou dans Windows 32 bits avec prise en charge NTVDM . Ne peut pas être exécuté "directement" (de manière transparente) sous n'importe quel Windows 64 bits, car un noyau x86-64 ne peut pas utiliser le mode vm86.
Construisez-le dans un
.com
exécutable pour qu'il soit chargécs:100h
avec tous les registres de segment égaux les uns aux autres (modèle de mémoire minuscule).Bonne chance.
la source
Cet exemple montre comment accéder directement à l'API Windows sans créer de lien dans la bibliothèque C Standard.
Pour compiler, vous aurez besoin de NASM et LINK.EXE (de Visual studio Standard Edition)
la source
gcc hello.obj
Ce sont des exemples Win32 et Win64 utilisant des appels d'API Windows. Ils sont pour MASM plutôt que NASM, mais regardez-les. Vous pouvez trouver plus de détails dans cet article.
Cela utilise MessageBox au lieu d'imprimer sur stdout.
MASM Win32
MASM Win64
Pour les assembler et les lier à l'aide de MASM, utilisez ceci pour un exécutable 32 bits:
ou ceci pour un exécutable 64 bits:
Pourquoi Windows x64 doit-il réserver 28h octets d'espace de pile avant un
call
? Cela représente 32 octets (0x20) d'espace fantôme, c'est-à-dire l'espace d'accueil, comme l'exige la convention d'appel. Et 8 octets supplémentaires pour réaligner la pile de 16, car la convention d'appel exige que RSP soit aligné sur 16 octets avant uncall
. (main
L'appelant de notre (dans le code de démarrage CRT) l'a fait. L'adresse de retour de 8 octets signifie que RSP est à 8 octets d'une limite de 16 octets à l'entrée d'une fonction.)L'espace fantôme peut être utilisé par une fonction pour vider ses arguments de registre à côté de l'endroit où se trouveraient les arguments de la pile (le cas échéant). A
system call
nécessite 30h (48 octets) pour réserver également de l'espace pour r10 et r11 en plus des 4 registres mentionnés précédemment. Mais les appels DLL ne sont que des appels de fonction, même s'ils enveloppent lessyscall
instructions.Fait amusant: non-Windows, c'est-à-dire que la convention d'appel x86-64 System V (par exemple sous Linux) n'utilise pas du tout d'espace fantôme, et utilise jusqu'à 6 arguments de registre entier / pointeur, et jusqu'à 8 arguments FP dans les registres XMM .
En utilisant la
invoke
directive MASM (qui connaît la convention d'appel), vous pouvez utiliser un ifdef pour en faire une version qui peut être construite en 32 bits ou 64 bits.La variante de macro est la même pour les deux, mais vous n'apprendrez pas l'assemblage de cette façon. Vous apprendrez plutôt l'asm de style C.
invoke
est pourstdcall
oufastcall
whilecinvoke
est pourcdecl
ou argument variablefastcall
. L'assembleur sait lequel utiliser.Vous pouvez désassembler la sortie pour voir son
invoke
extension.la source
title
comme nom d'étiquette, je rencontre des erreurs. Cependant, lorsque j'utilise autre chose comme nom d'étiquettemytitle
, tout fonctionne bien.Flat Assembler n'a pas besoin d'un linker supplémentaire. Cela rend la programmation de l'assembleur assez facile. Il est également disponible pour Linux.
Ceci est
hello.asm
tiré des exemples Fasm:Fasm crée un exécutable:
Et voici le programme de l' IDA :
Vous pouvez voir les trois appels:
GetCommandLine
,MessageBox
etExitProcess
.la source
Pour obtenir un .exe avec le compilateur NASM et l'éditeur de liens de Visual Studio, ce code fonctionne correctement:
Si ce code est enregistré sur par exemple "test64.asm", alors pour compiler:
Produit "test64.obj" Puis pour créer un lien à partir de l'invite de commande:
où path_to_link pourrait être C: \ Program Files (x86) \ Microsoft Visual Studio 10.0 \ VC \ bin ou où se trouve votre programme link.exe sur votre ordinateur , path_to_libs pourrait être C: \ Program Files (x86) \ Windows Kits \ 8.1 \ Lib \ winv6.3 \ um \ x64 ou où se trouvent vos bibliothèques (dans ce cas, kernel32.lib et user32.lib sont au même endroit, sinon utilisez une option pour chaque chemin dont vous avez besoin) et / largeaddressaware: aucune option n'est nécessaire pour éviter de se plaindre des adresses trop longues (pour user32.lib dans ce cas). De plus, comme cela est fait ici, si l'éditeur de liens de Visual est appelé à partir de l'invite de commande, il est nécessaire de configurer l'environnement précédemment (exécutez une fois vcvarsall.bat et / ou voir MS C ++ 2010 et mspdb100.dll).
la source
default rel
en haut de votre fichier afin que ces modes d'adressage ([msg]
et[title]
) utilisent l'adressage relatif RIP au lieu de 32 bits absolu.À moins que vous n'appeliez une fonction, ce n'est pas du tout trivial. (Et, sérieusement, il n'y a pas de réelle différence de complexité entre l'appel de printf et l'appel d'une fonction api win32.)
Même DOS int 21h n'est en réalité qu'un appel de fonction, même s'il s'agit d'une API différente.
Si vous voulez le faire sans aide, vous devez parler directement à votre matériel vidéo, probablement en écrivant des bitmaps des lettres "Hello world" dans un framebuffer. Même alors, la carte vidéo effectue le travail de traduction de ces valeurs de mémoire en signaux VGA / DVI.
Notez qu'en réalité, rien de tout cela jusqu'au matériel n'est plus intéressant en ASM qu'en C. Un programme "hello world" se résume à un appel de fonction. Une bonne chose à propos de l'ASM est que vous pouvez utiliser n'importe quel ABI que vous voulez assez facilement; vous avez juste besoin de savoir ce qu'est cet ABI.
la source
Les meilleurs exemples sont ceux avec fasm, car fasm n'utilise pas d'éditeur de liens, ce qui cache la complexité de la programmation Windows par une autre couche opaque de complexité. Si vous êtes satisfait d'un programme qui écrit dans une fenêtre d'interface graphique, alors il y a un exemple pour cela dans le répertoire d'exemples de fasm.
Si vous voulez un programme console, qui permet la redirection de l'entrée standard et de la sortie standard, cela est également possible. Il existe un programme d'exemple (hélas très non trivial) disponible qui n'utilise pas d'interface graphique et qui fonctionne strictement avec la console, c'est-à-dire le fasm lui-même. Cela peut être réduit à l'essentiel. (J'ai écrit un quatrième compilateur qui est un autre exemple non-gui, mais ce n'est pas non plus trivial).
Un tel programme a la commande suivante pour générer un en-tête exécutable approprié, normalement effectué par un éditeur de liens.
Une section appelée «.idata» contient une table qui aide les fenêtres au démarrage à coupler les noms de fonctions aux adresses d'exécution. Il contient également une référence à KERNEL.DLL qui est le système d'exploitation Windows.
Le format de table est imposé par les fenêtres et contient des noms qui sont recherchés dans les fichiers système, au démarrage du programme. FASM cache une partie de la complexité derrière le mot-clé rva. Ainsi, _ExitProcess @ 4 est une étiquette fasm et _exitProcess est une chaîne recherchée par Windows.
Votre programme est dans la section '.text'. Si vous déclarez que cette section est accessible en écriture et exécutable, c'est la seule section que vous devez ajouter.
Vous pouvez appeler toutes les installations que vous avez déclarées dans la section .idata. Pour un programme console, vous avez besoin de _GetStdHandle pour trouver les descripteurs de fichiers pour les entrées standard et standardout (en utilisant des noms symboliques tels que STD_INPUT_HANDLE que fasm trouve dans le fichier include win32a.inc). Une fois que vous avez les descripteurs de fichiers, vous pouvez faire WriteFile et ReadFile. Toutes les fonctions sont décrites dans la documentation de kernel32. Vous en êtes probablement conscient ou vous n'essaieriez pas de programmer l'assembleur.
En résumé: il existe un tableau avec des noms asci qui correspondent au système d'exploitation Windows. Au démarrage, cela se transforme en une table d'adresses appelables, que vous utilisez dans votre programme.
la source
Si vous souhaitez utiliser NASM et l'éditeur de liens de Visual Studio (link.exe) avec l'exemple Hello World d'anderstornvig, vous devrez créer un lien manuel avec la bibliothèque C Runtime qui contient la fonction printf ().
J'espère que cela aide quelqu'un.
la source