Lectures complémentaires pour l'un des sujets ici: Le guide définitif des appels système Linux
J'ai vérifié ces derniers en utilisant GNU Assembler (gas) sous Linux.
Interface du noyau
Convention d'appel système Linux x86-32 aka i386:
Dans x86-32, les paramètres pour l'appel système Linux sont passés à l'aide de registres. %eax
pour syscall_number. % ebx,% ecx,% edx,% esi,% edi,% ebp sont utilisés pour passer 6 paramètres aux appels système.
La valeur de retour est dans %eax
. Tous les autres registres (y compris EFLAGS) sont conservés dans le int $0x80
.
J'ai pris l'extrait suivant du tutoriel d'assemblage Linux, mais j'en doute. Si quelqu'un peut montrer un exemple, ce serait formidable.
S'il y a plus de six arguments,
%ebx
doit contenir l'emplacement mémoire où la liste des arguments est stockée - mais ne vous inquiétez pas à ce sujet car il est peu probable que vous utilisiez un appel système avec plus de six arguments.
Pour un exemple et un peu plus de lecture, reportez-vous à http://www.int80h.org/bsdasm/#alternate-calling-convention . Un autre exemple de Hello World pour i386 Linux utilisant int 0x80
: Hello, world en langage assembleur avec des appels système Linux?
Il existe un moyen plus rapide de passer des appels système 32 bits: utiliser sysenter
. Le noyau mappe une page de mémoire dans chaque processus (le vDSO), avec le côté espace utilisateur de la sysenter
danse, qui doit coopérer avec le noyau pour qu'il puisse trouver l'adresse de retour. Arg pour enregistrer le mappage est le même que pour int $0x80
. Vous devez normalement appeler le vDSO au lieu d'utiliser sysenter
directement. (Voir The Definitive Guide to Linux System Calls pour plus d'informations sur la liaison et l'appel dans le vDSO, et pour plus d'informations sur sysenter
, et tout ce qui concerne les appels système.)
x86-32 [Free | Open | Net | DragonFly] Convention d'appel système BSD UNIX:
Les paramètres sont passés sur la pile. Poussez les paramètres (le dernier paramètre poussé en premier) sur la pile. Ensuite, envoyez 32 bits supplémentaires de données factices (ce ne sont pas réellement des données factices. Reportez-vous au lien suivant pour plus d'informations), puis donnez une instruction d'appel systèmeint $0x80
http://www.int80h.org/bsdasm/#default-calling-convention
Convention d'appel système Linux x86-64:
x86-64 Mac OS X est similaire mais différent . TODO: vérifiez ce que fait * BSD.
Reportez - vous à la section: "A.2 AMD64 Linux Conventions du noyau" du système binaire d' application V Interface Architecture AMD64 Supplément processeur . Les dernières versions des psABI i386 et x86-64 System V peuvent être trouvées liées à partir de cette page dans le repo du responsable ABI . (Voir aussi lex86 tag wiki pour les liens ABI à jour et beaucoup d'autres bonnes choses sur x86 asm.)
Voici l'extrait de cette section:
- Les applications de niveau utilisateur utilisent comme registres d'entiers pour transmettre la séquence% rdi,% rsi,% rdx,% rcx,% r8 et% r9. L'interface du noyau utilise% rdi,% rsi,% rdx,% r10,% r8 et% r9.
- Un appel système se fait via l'
syscall
instruction . Cela supprime% rcx et% r11 ainsi que la valeur de retour% rax, mais les autres registres sont conservés.
- Le numéro de l'appel système doit être passé dans le registre% rax.
- Les appels système sont limités à six arguments, aucun argument n'est passé directement sur la pile.
- En revenant de l'appel système, le registre% rax contient le résultat de l'appel système. Une valeur comprise entre -4095 et -1 indique une erreur, c'est le cas
-errno
.
- Seules les valeurs de la classe INTEGER ou de la classe MEMORY sont transmises au noyau.
Rappelez-vous que cela provient de l'annexe spécifique à Linux de l'ABI, et même pour Linux, c'est informatif et non normatif. (Mais c'est en fait exact.)
Cet int $0x80
ABI 32 bits est utilisable en code 64 bits (mais fortement déconseillé). Que se passe-t-il si vous utilisez l'ABI Linux int 0x80 32 bits dans un code 64 bits? Il tronque toujours ses entrées en 32 bits, il ne convient donc pas aux pointeurs et met à zéro r8-r11.
Interface utilisateur: appel de fonction
Convention d'appel de fonction x86-32:
Dans x86-32, les paramètres étaient passés sur la pile. Le dernier paramètre a été poussé en premier sur la pile jusqu'à ce que tous les paramètres soient terminés, puis l' call
instruction a été exécutée. Ceci est utilisé pour appeler les fonctions de la bibliothèque C (libc) sur Linux à partir de l'assembly.
Les versions modernes de l'ABI i386 System V (utilisé sous Linux) nécessitent un alignement de 16 octets %esp
avant a call
, comme l'ABI System V x86-64 l'a toujours exigé. Les appelées sont autorisés à supposer que et à utiliser les charges / stockages SSE 16 octets qui échouent sur non aligné. Mais historiquement, Linux ne nécessitait qu'un alignement de pile de 4 octets, il fallait donc un travail supplémentaire pour réserver un espace naturellement aligné, même pour un 8 octets double
ou quelque chose du genre.
Certains autres systèmes 32 bits modernes ne nécessitent toujours pas d'alignement de pile de plus de 4 octets.
x86-64 System V espace utilisateur Fonction Convention d'appel:
x86-64 System V passe les arguments dans les registres, ce qui est plus efficace que la convention d'arguments de pile d'i386 System V. Cela évite la latence et les instructions supplémentaires de stockage des arguments en mémoire (cache), puis de les charger à nouveau dans l'appelé. Cela fonctionne bien car il y a plus de registres disponibles et c'est mieux pour les processeurs modernes hautes performances où la latence et l'exécution dans le désordre sont importantes. (Le i386 ABI est très ancien).
Dans ce nouveau mécanisme: Premièrement, les paramètres sont divisés en classes. La classe de chaque paramètre détermine la manière dont il est passé à la fonction appelée.
Pour des informations complètes, reportez-vous à: "3.2 Function Calling Sequence" du supplément de processeur d'architecture AMD64 de l'interface binaire d'application System V qui lit, en partie:
Une fois les arguments classés, les registres sont affectés (dans l'ordre de gauche à droite) pour être transmis comme suit:
- Si la classe est MEMORY, passez l'argument sur la pile.
- Si la classe est INTEGER, le prochain registre disponible de la séquence% rdi,% rsi,% rdx,% rcx,% r8 et% r9 est utilisé
Ainsi , %rdi, %rsi, %rdx, %rcx, %r8 and %r9
les registres de commande utilisés pour transmettre entier / pointeur paramètres ( par exemple classe INTEGER) à toute fonction libc de montage. % rdi est utilisé pour le premier paramètre INTEGER. % rsi pour le 2ème,% rdx pour le 3ème et ainsi de suite. Ensuite, des call
instructions devraient être données. La pile ( %rsp
) doit être alignée sur 16B lors de l' call
exécution.
S'il y a plus de 6 paramètres INTEGER, le 7ème paramètre INTEGER et les suivants sont passés sur la pile. (L'appelant apparaît, identique à x86-32.)
Les 8 premiers arguments à virgule flottante sont passés dans% xmm0-7, plus tard sur la pile. Il n'y a pas de registres vectoriels préservés des appels. (Une fonction avec un mélange d'arguments FP et entiers peut avoir plus de 8 arguments de registre au total.)
Les fonctions variadiques ( commeprintf
) ont toujours besoin %al
du nombre d'arguments du registre FP.
Il existe des règles pour conditionner les structures dans des registres ( rdx:rax
au retour) ou en mémoire. Consultez l'ABI pour plus de détails et vérifiez la sortie du compilateur pour vous assurer que votre code est d'accord avec les compilateurs sur la manière dont quelque chose doit être transmis / retourné.
Notez que la convention d'appel de la fonction Windows x64 présente plusieurs différences significatives par rapport au système x86-64 System V, comme l'espace d'ombre qui doit être réservé par l'appelant (au lieu d'une zone rouge) et l'appel préservé xmm6-xmm15. Et des règles très différentes pour lesquelles arg va dans quel registre.