J'essaie de lier l'assemblage x86 et C.
Mon programme C:
extern int plus_10(int);
# include <stdio.h>
int main() {
int x = plus_10(40);
printf("%d\n", x);
return 0;
}
Mon programme d'assemblage:
[bits 32]
section .text
global plus_10
plus_10:
pop edx
mov eax, 10
add eax, edx
ret
Je compile et lie les deux comme suit:
gcc -c prog.c -o prog_c.o -m32
nasm -f elf32 prog.asm -o prog_asm.o
gcc prog_c.o prog_asm.o -m32
Cependant, lorsque j'exécute le fichier résultant, j'obtiens une erreur de segmentation.
Mais quand je remplace
pop edx
avec
mov edx, [esp + 4]
le programme fonctionne bien. Quelqu'un peut-il expliquer pourquoi cela se produit?
Ceci est un code d'assemblage possible de int x = plus_10(40);
Push 40 ; Push argument
call plus_10 ; call function
retadd: add esp, 4 ; clean up stack (dummy pop)
; result of the function call is in EAX, per the calling convention
; if compiled without optimization, the caller might just store it:
mov DWORD PTR [ebp-x], eax ; store return value
; (in eax) in x
Maintenant, lorsque vous appelez plus_10
, l'adresse retadd
est poussée sur la pile par l'instruction call
. C'est en fait un Push
+ jmp
, et ret
est effectivement pop eip
.
Donc, votre pile ressemble à ceci dans le plus_10
fonction:
| ... |
+--------+
| 40 | <- ESP+4 points here (the function argument)
+--------+
| retadd | <- ESP points here
+--------+
ESP
pointe vers un emplacement mémoire qui contient l'adresse de retour.
Maintenant, si vous utilisez pop edx
l'adresse de retour va dans edx
et la pile ressemble à ceci:
| ... |
+--------+
| 40 | <- ESP points here
+--------+
Maintenant, si vous exécutez ret
à ce stade, le programme sautera en fait à l'adresse 40 et très probablement au défaut de segmentation ou se comportera d'une autre manière imprévisible.
Le code d'assembly réel généré par le compilateur peut être différent, mais cela illustre le problème.
BTW, une façon plus efficace d'écrire votre fonction est la suivante: c'est ce que la plupart des compilateurs feraient avec l'optimisation activée, pour une version non en ligne de cette petite fonction.
global plus_10
plus_10:
mov eax, [esp+4] ; retval = first arg
add eax, 10 ; retval += 10
ret
C'est plus petit et légèrement plus efficace que
mov eax, 10
add eax, [esp+4] ; decode to a load + add.
ret