IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

x86 32-bits / 64-bits Assembleur Discussion :

[NASM / Linux 64] Utiliser les fonctions standard du C


Sujet :

x86 32-bits / 64-bits Assembleur

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre confirmé
    Inscrit en
    Avril 2005
    Messages
    156
    Détails du profil
    Informations forums :
    Inscription : Avril 2005
    Messages : 156
    Par défaut [NASM / Linux 64] Utiliser les fonctions standard du C
    Bonjour,

    Je suis débutant en programmation assembleur, j'utilise NASM sous Ubuntu 64 pour créer des programmes 64 bits et il m'est impossible d'utiliser les fonctions standard du C (printf en l'occurence)...Voici mon code :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    section .data
     
    	s_err: db 'Use: ./echo <string>', 0xA
    	s_err_len: equ $ - s_err
    	hello: db 'Hello world!', 0xA
    	helloLen: equ $ - hello
    	s_test: db 'Hello !', 0xA, 0x0
     
    section .text
     
    	global _start
    	extern printf
     
    	_start:
     
    		; Checking argc
     
    		pop rax
     
    		cmp rax, 2
    		je args_ok
     
    		args_nok:
     
    		; Displaying error message (sys_write)
    		mov rax, 4
    		mov rbx, 2
    		mov rcx, s_err
    		mov rdx, s_err_len
    		int 80h
     
    		; Exiting (1)
    		mov rax, 1
    		mov rbx, 1
    		int 80h
     
    		args_ok:
     
    		;  TODO
    		push s_test
    		call printf
    		pop rax
     
    		; Exiting program (0)
    		mov rax, 1
    		mov rbx, 0
    		int 80h
    Je créé l'exécutable de cette façon :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    nasm -f elf64 echo.asm
    ld -s -m elf_x86_64 -o echo echo.o -lc
    A l'exécution :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    ./echo
    bash: ./echo: Aucun fichier ou dossier de ce type
    Quel est le problème ? Si je vire l'appel à printf dans le code assembleur et le lien avec la bibliothèque C, ça fonctionne...

    Edit : je précise que le fichier echo existe bien dans le répertoire et qu'il est exécutable !

    Merci de votre aide !

  2. #2
    Membre chevronné Avatar de dapounet
    Profil pro
    Étudiant
    Inscrit en
    Juillet 2007
    Messages
    469
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Juillet 2007
    Messages : 469
    Par défaut
    Bonjour,

    En 64 bits les paramètres sont passés par les registes autant que possible. Je ne peux pas tester maintenant, mais ce code devrait fonctionner :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    mov rcx, s_test
    xor eax, eax
    call printf
    RCX contient le premier paramètre.
    Pour les fonctions comme printf() dont le compilateur ne peut pas deviner quels paramètres seront passés, il faut mettre dans RAX le nombre de paramètres qui sont passés par les registres XMM, ici 0. xor eax, eax fait la même chose que xor rax, rax ; quand on ne modifie que les 32 bits de poids faible d'un registre 64 bits, la partie supérieure est mise à 0.

    Tous les détails sur les conventions d'appel sont dans ce document : http://agner.org/optimize/calling_conventions.pdf.

  3. #3
    Membre confirmé
    Inscrit en
    Avril 2005
    Messages
    156
    Détails du profil
    Informations forums :
    Inscription : Avril 2005
    Messages : 156
    Par défaut
    Merci beaucoup pour ces infos, malheureusement cela n'arrange rien. En fait le problème vient du lien avec la librairie C puisque même en virant l'appel à printf, j'ai le même problème.

    En virant le "-lc" lors de l'édition des liens ça fonctionne (en ayant viré également les appels à printf dans le code, sinon j'ai bien sûr le message "undefined reference to printf").

    Une idée ?

  4. #4
    Membre habitué
    Profil pro
    Inscrit en
    Janvier 2009
    Messages
    13
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2009
    Messages : 13
    Par défaut j'ai fait un test qui fonctionne...
    Bonjour,

    Je me permet de vous répondre car je cherchais aussi une solution.

    Je me suis permis de regarder le document envoyé par dapounet. Ce document est vraiment bien et ce type d'information m'intéresse car je suis en train de découvrir l'assembleur en 64bits.

    De mon côté j'ai simplifié l'appel à la fonction 'puts'.

    J'ai d'abord fait un premier code ou je transmettais le paramètre de la fonction par la pile, comme en 32bits... ça compile, ça link, mais ça marche pas : bash: ./echo: Aucun fichier ou dossier de ce type

    Alors j'ai testé avec le registre rcx... ça marche toujours pas, mais c'est mieux : segmentation fault

    Puis, d'après la doc, ce serait 'rdi' le premier registre à utiliser... et ça marche !

    voici mon petit programme de test (hello.asm)

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
     
    section .data
    	hello:     db 'Hello world!',0    ; 'Hello world!' terminé par le fameux \0 pour le C
     
    section .text
            extern puts                     ; déclaration de la fonction puts
     
    	global _start
     
    _start:
     
    	mov rdi,  hello     ; store the parameter in 
    	xor eax, eax       ; je ne sais pas si cela sert ?
    	call puts		; appel de la fonction puts !
     
     
     
    	mov eax,1            ;  system call pour exit (sys_exit)
    	mov ebx,0            ; Exit avec return code of 0 (no error)
    	int 80h
    pour compiler le tout :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
     nasm -f elf64 hello.asm
    puis l'édition des liens :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
     ld --dynamic-linker /lib/ld-linux-x86-64.so.2 -lc -o hello hello.o
    Voilà, je n'ai pas encore tout compris, mais c'est un début...

    l'ordre des paramètres est :
    rdi, rsi, rdx, rcx, r8, r9, xmm0-7

    donc si vous transmettez la valeur à afficher et le format avant le call à printf sur rdi,rsi (il faudra peut-être inverser l'ordre des paramètres car je n'ai pas compris tout dans le document).

    merci de me dire comment vous avez fait si vous y arrivez.

    majµcarma

  5. #5
    Membre confirmé
    Inscrit en
    Avril 2005
    Messages
    156
    Détails du profil
    Informations forums :
    Inscription : Avril 2005
    Messages : 156
    Par défaut
    Merci !

    L'ajout de l'option "--dynamic-linker [...]" lors de l'édition des liens a réglé le problème ! Un exemple d'utilisation de la fonction C printf :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    section .data
     
    	s_frmt: db 'Hello %s!', 0xA, 0x0
    	s_name: db 'majucarma', 0x0
     
    section .text
     
    	global _start
    	extern printf
     
    	_start:
     
    		mov rdi, s_frmt      ; format
    		mov rsi, s_name   ; name
    		xor rax, rax          ; rax <- 0
    		call printf
     
    		; Exiting program (0)
    		mov rax, 1
    		mov rbx, 0
    		int 80h
    Les arguments sont donc transmis en utilisant les registres que vous indiquez, dans le même ordre. Et comme l'indique dapounet, il faut mettre dans RAX le nombre d'arguments qui sont passés par les registres XMM pour les fonctions variadiques (nombre d'arguments variable), dont puts ne fait pas partie, RAX n'a donc pas besoin d'être modifié.

    Une petite question par rapport à ceci : pourquoi

    plutôt que

    ?

  6. #6
    Membre habitué
    Profil pro
    Inscrit en
    Janvier 2009
    Messages
    13
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2009
    Messages : 13
    Par défaut ça va finir par marcher !
    Je pense qu'il s'agit d'une vieille habitude...

    Avant, un "mov ax, 0" nécessitait plus de cycle processeur qu'un xor ax,ax. Ce qui aujourd'hui, d'après les doc des constructeurs n'est plus vraiment exact.

    Là où l'on gagne un peu, c'est sur la taille finale du programme (mais j'en suis pas certain à 100% pour le mode 64bits). En tout cas, autrefois lorsque nous programmions encore en 32bits, un mov eax,<valeur_entière> pouvait prendre de 1 à 4 octets en mémoire selon la la taille dudit nombre à copier. alors qu'un xor ax, ax représente un simple octet dans 100% des cas...

    Ici dans notre exemple, il va de soit que cela ne change pas grand chose...

    D'ailleur, je pense que nous ne sommes pas sortis de l'auberge. En fait, quand on passe une valeur entière, ça marche plus ! A priori, la difficuté ne provient pas de l'assembleur, mais plutot du C. En effet, printf peut avoir un nombre d'arguments variables. Je bouquine la doc du constructeur mais je n'ai rien trouvé à ce sujet.

    Au fait, la doc est disponible en ligne : (5 volumes de 400 pages en moyenne...)
    http://developer.amd.com/documentati...s/default.aspx


    Je n'ai pas vraiment pu me pencher sur le problème, mais cela vaut la peine de chercher. Je le fait de temps en temps donc ça va pas vite .

    En tout cas, si tu débutes en assembleur, je te conseille de commencer par t'habituer avec des compilateurs comme MASM ou TASM en mode 16bits sous DosBox. Il y a beaucoup plus de docs sur le sujet. C'est relativement simple comparé au monstre que nous essayons de dompter... Ensuite, les techniques acquises te serviront pour les modes 32 ou 64bits Linux ou Windows.

    Voilà.

    majµcarma

Discussions similaires

  1. Réponses: 14
    Dernier message: 26/06/2013, 18h03
  2. comment utiliser les fonctions d'une dll
    Par sebled dans le forum MFC
    Réponses: 3
    Dernier message: 24/02/2006, 16h59
  3. [Conception] Utiliser les fonctions des tableaux ou plusieurs requêtes ?
    Par Derik dans le forum PHP & Base de données
    Réponses: 10
    Dernier message: 01/02/2006, 09h54
  4. Réponses: 3
    Dernier message: 31/12/2005, 23h09
  5. Réponses: 11
    Dernier message: 22/12/2003, 21h06

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo