web-dev-qa-db-fra.com

Pointeurs d'assemblage x86

j'espère que ce n'est pas une question stupide, mais j'essaie de me concentrer sur les conseils à l'Assemblée.

Quelle est exactement la différence entre:

mov eax, ebx

et

mov [eax], ebx

et quand faut-il dword ptr [eax] Devrait être utilisé?

Aussi quand j'essaye de faire mov eax, [ebx] J'obtiens une erreur de compilation, pourquoi?

14
Duxa

Comme cela a déjà été dit, le fait de placer des crochets autour d'un opérande signifie que cet opérande doit être déréférencé , comme s'il s'agissait d'un pointeur en C. En d'autres termes , les crochets signifient que vous lisez une valeur de (ou stockez une valeur dans ) cet emplacement de mémoire, plutôt que de lire directement cette valeur.

Donc ça:

mov  eax, ebx

copie simplement la valeur de ebx dans eax. Dans une notation pseudo-C, ce serait: eax = ebx.

Attendu que:

mov  eax, [ebx]

déréférence le contenu de ebx et stocke la valeur pointée dans eax. Dans une notation pseudo-C, ce serait: eax = *ebx.

Enfin, ceci:

mov  [eax], ebx

stocke la valeur dans ebx dans l'emplacement mémoire indiqué par eax. Encore une fois, en notation pseudo-C: *eax = ebx.


Les registres ici pourraient également être remplacés par des opérandes de mémoire, tels que des noms de variables symboliques. Donc ça:

mov  eax, [myVar]

déréférence l'adresse de la variable myVar et stocke le contenu de cette variable dans eax, comme eax = myVar.

En revanche, ceci:

mov  eax, myVar

stocke l'adresse de la variable myVar dans eax, comme eax = &myVar.

C'est du moins ainsi que la plupart des assembleurs fonctionnent. L'assembleur de Microsoft (appelé MASM) et l'assemblage en ligne du compilateur Microsoft C/C++ sont un peu différents. Il traite les deux instructions ci-dessus comme équivalentes, essentiellement en ignorant les crochets autour des opérandes de mémoire.

Pour obtenir l'adresse d'une variable dans MASM, vous devez utiliser le mot clé OFFSET:

mov  eax, OFFSET myVar

Cependant, même si MASM a cette syntaxe indulgente et vous permet d'être bâclé, vous ne devriez pas. Incluez toujours les crochets lorsque vous souhaitez déréférencer une variable et obtenir sa valeur réelle. Vous n'obtiendrez jamais le mauvais résultat si vous écrivez le code explicitement en utilisant la syntaxe appropriée, et cela facilitera la compréhension des autres. De plus, cela vous obligera à prendre l'habitude d'écrire le code de la façon dont les autres assembleurs s'attendront à ce qu'il soit écrit, plutôt que de compter sur la béquille "faites ce que je veux dire, pas ce que j'écris" de MASM.

En parlant de cette béquille "faites ce que je veux dire, pas ce que j'écris", MASM vous permet également généralement de vous passer de l'omission du spécificateur de taille d'opérande, car il connaît la taille de la variable. Mais encore une fois, je recommande de l'écrire pour plus de clarté et de cohérence. Par conséquent, si myVar est un int, vous feriez:

mov  eax, DWORD PTR [myVar]    ; eax = myVar

ou

mov  DWORD PTR [myVar], eax    ; myVar = eax

Cette notation est nécessaire dans d'autres assembleurs comme NASM qui ne sont pas fortement typés et ne se souviennent pas que myVar est un emplacement mémoire de taille DWORD.

Vous n'en avez pas du tout besoin pour déréférencer les opérandes de registre, car le nom du registre indique sa taille. al et ah sont toujours de taille BYTE, ax est toujours de taille Word, eax est toujours DWORD - et rax est toujours de taille QWORD. Mais cela ne fait pas de mal de l'inclure de toute façon, si vous le souhaitez, par cohérence avec la façon dont vous notez les opérandes de la mémoire.


Aussi quand j'essaye de faire mov eax, [ebx] J'obtiens une erreur de compilation, pourquoi?

Tu ne devrais pas. Cela s'assemble très bien pour moi dans l'assemblage en ligne de MSVC. Comme nous l'avons déjà vu, cela équivaut à:

mov  eax, DWORD PTR [ebx]

et signifie que l'emplacement de mémoire pointé par ebx sera déréférencé et que la valeur de DWORD sera chargée dans eax.


pourquoi je ne peux pas faire mov a, [eax] Cela ne devrait-il pas faire de "a" un pointeur vers le pointage de eax?

Non. Cette combinaison d'opérandes n'est pas autorisée. Comme vous pouvez le voir dans la documentation de l'instruction MOV , il existe essentiellement cinq possibilités (en ignorant les encodages et segments alternatifs):

mov  register, register     ; copy one register to another
mov  register, memory       ; load value from memory into register
mov  memory,   register     ; store value from register into memory
mov  register, immediate    ; move immediate value (constant) into register
mov  memory,   immediate    ; store immediate value (constant) in memory

Notez qu'il n'y a pas de mov memory, memory, c'est ce que vous essayiez.

Cependant, vous pouvez faire pointer a vers ce que pointe eax en codant simplement:

mov  DWORD PTR [a], eax

Maintenant, a et eax ont la même valeur. Si eax était un pointeur, alors a est maintenant un pointeur vers ce même emplacement mémoire.

Si vous souhaitez définir a sur la valeur vers laquelle eax pointe, alors vous devrez faire:

mov  eax, DWORD PTR [eax]    ; eax = *eax
mov  DWORD PTR [a], eax      ; a   = eax

Bien sûr, cela encombre le pointeur et le remplace par la valeur déréférencée. Si vous ne voulez pas perdre le pointeur, vous devrez utiliser un deuxième registre "scratch"; quelque chose comme:

mov  edx, DWORD PTR [eax]    ; edx = *eax
mov  DWORD PTR [a], edx      ; a   = edx

Je me rends compte que tout cela est quelque peu déroutant. L'instruction mov est surchargée d'un grand nombre de significations potentielles dans l'ISA x86. Cela est dû aux racines de x86 en tant qu'architecture CISC. En revanche, les architectures RISC modernes font un meilleur travail de séparation des mouvements registre-registre, des charges de mémoire et des magasins de mémoire. x86 les rassemble en une seule instruction mov. Il est trop tard pour revenir en arrière et le réparer maintenant; il suffit de se familiariser avec la syntaxe, et parfois cela prend un deuxième coup d'œil.

22
Cody Gray