2017-07-28 1 views
0

J'apprends assemblage en utilisant les éléments suivants bonjour programme mondialComment imprimer la longueur d'une chaîne dans l'assemblage

section .text 
    global _start  ;must be declared for linker (ld) 

_start:    ;tells linker entry point 
    mov edx,len  ;message length 
    mov ecx,msg  ;message to write 
    mov ebx,1  ;file descriptor (stdout) 
    mov eax,4  ;system call number (sys_write) 
    int 0x80  ;call kernel 

    mov eax,1  ;system call number (sys_exit) 
    int 0x80  ;call kernel 

section .data 
    msg db 'Hello, world!', 0xa ;our string 
    len equ $ - msg    ;length of our string 

La question initiale que j'avais été ce qu'on entend par la longueur de la chaîne. Cela signifie-t-il le nombre de caractères ou la longueur en mémoire (nombre d'octets)? Pour vérifier cela, j'ai voulu imprimer la variable len. Comment puis je faire ça? Je naïvement essayé de définir la nouvelle variable

len2 equ $ - len 

puis à la place courir

mov edx,len2  ;message length 
    mov ecx,len  ;message to write 
    mov ebx,1  ;file descriptor (stdout) 
    mov eax,4  ;system call number (sys_write) 
    int 0x80  ;call kernel 

pour essayer d'imprimer len, mais rien sérigraphiés. Comment puis-je imprimer le nombre représenté par len?

+5

Vous pouvez vérifier [** Comment imprimer un numéro dans l'assemblage NASM? **] (https://stackoverflow.com/questions/8194141/how-to-print-a-number-in-assembly- nasm) La réponse courte est que vous écrivez * caractères * à 'stdout'. Un nombre est une valeur décimale, pas une chaîne de caractères que vous pouvez écrire immédiatement, vous devez convertir la valeur entière ou non signée en représentation de son caractère, puis l'écrire dans 'stdout'. –

+0

Par curiosité pour quel système d'exploitation programmez-vous? Les fenêtres? Unix? MacOS? Je demande parce qu'en plus des questions comme ceci souvent les personnes demandant sur SO utilisent des tutoriels pour la plate-forme fausse. –

+0

Merci. J'utilise ubuntu. Par curiosité, quand j'essaie d'imprimer directement le numéro en utilisant stdout, je n'ai rien. Pourquoi n'imprime-t-il rien au lieu d'imprimer des bêtises? –

Répondre

0
... 
    mov edx,len  ;message length 

Ce charges edx avec une sorte de valeur numérique, comme 14 dans ce cas. len est "EQU" symbole constant, quelque chose comme #define en C.

mov ecx,msg  ;message to write 

Cette charge ecx avec l'adresse du premier caractère (msg est étiquette, pointant en mémoire).

mov ebx,1  ;file descriptor (stdout) 
    mov eax,4  ;system call number (sys_write) 
    int 0x80  ;call kernel 
    ... 

    msg db 'Hello, world!', 0xa ;our string 

Ceci définit 14 octets de mémoire, avec des valeurs 72 ('H'), 101 ('e'), .... Le premier octet est pointé par msg étiquette (adresse mémoire de celui-ci).

len equ $ - msg    ;length of our string 

Ceci définit la constante len visible pendant la compilation. Il ne définit aucun contenu de mémoire, donc vous ne pouvez pas le trouver dans l'exécutable ou pendant l'exécution (à moins d'être utilisé, comme par exemple mov edx,len, alors il est compilé dans cette instruction particulière bien sûr).

La définition est $ - msg, le $ dans ce contexte fonctionne comme « l'adresse actuelle », où sera compilé l'octet suivant de code machine définie, donc à cet endroit, il est égal à msg + 14 (je l'espère, je l'ai fait compter le nombre de caractères correctement :)). Et ((msg+14) - msg) = 14 = nombre d'octets définis dans la mémoire entre la définition de len et l'étiquette msg.

Remarquez comment j'évite les mots comme variables ou caractères, l'ASM est plus bas niveau, donc les étiquettes en mémoire et octets sont des mots plus précis et j'espère qu'il vous aidera à reconnaître les différences subtiles.

Votre len2 equ $ - len après la len ne donc définir la valeur len2 comme (msg+14) (toujours là dans la mémoire, aucun nouvel octet ajouté len définition) moins len qui est 14, de sorte que vous effectivement défini len2 égal à msg.

Puis:

mov edx,len2  ;message length 
    mov ecx,len  ;message to write 
    ... 

n'appelle sys_write avec pointeur vers la chaîne égale à 14 (référence mémoire non valide, cette zone de mémoire est interdite au code utilisateur ordinaire) et longueur égale à traiter msg, qui sera être sur 32b linux très probablement une valeur comme 0x80004000, soit plus de 2G de caractères à la sortie.

Le sys_write n'aime pas cela, échoue et renvoie le code d'erreur dans eax.

Pour quoi que ce soit de sortie à la console avec sys_write vous devez d'abord écrire dans la mémoire comme ASCII (je pense que UTF8 est prise en charge par défaut dans shell Ubuntu, mais trop paresseux pour vérification) chaîne codée et donner l'adresse sys_write de cette mémoire , et la longueur en octets (avec la chaîne UTF8 la différence entre les octets et les caractères est importante, sys_write ne connaît pas les caractères, cela fonctionne avec les fichiers binaires et les octets, donc la longueur est la quantité d'octets). Je ne vais pas écrire de code sur les numéros de sortie, car cela fait plusieurs lignes (implémentation printf simplifiée) et SO a plusieurs Q + A, mais j'espère que mon explication vous aidera à comprendre ce qui s'est passé et comment Ça marche.

Si vous êtes en train d'apprendre ASM, envisager soit de lier l'clib d'avoir printf disponibles, ou mieux encore, utilisez débogueur, et vérifier les valeurs droites dans les registres Débogueur, ne vous embêtez pas avec la sortie de chaîne encore, c'est un peu sujet plus avancé que l'arithmétique initiale, et le contrôle de flux de base et la pile d'exploitation. Une fois que vous serez plus à l'aise avec le fonctionnement de l'instruction de base et la manière de déboguer le code, il sera plus facile d'essayer de sortir les numéros.