C'est très basique, mais je ne le savais pas jusqu'à présent, alors prenez note.
Lorsque j'ai défini un point d'arrêt sur une fonction dans GDB, je ne savais pas où l'arrêter au niveau du langage machine. Immédiatement avant l'appel de l'appelant? Immédiatement après l'appel? Après avoir appuyé sur le pointeur de la base? Après avoir sécurisé la zone pour les variables locales?
De la conclusion, c'était «après avoir alloué la surface de la variable locale». (Autrement dit, le traitement de trame de pile minimum requis est exécuté lorsqu'il est appelé)
La langue (standard) est C99.
environnement
gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.11)
Linux Vega 4.4.0-131-generic #157-Ubuntu SMP Thu Jul 12 15:51:36 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux
Code de confirmation
#include <stdio.h>
int func(int a, int b){
int c;
c = a + b;
return(2 * c);
}
int main(void){
int n;
n = func(1, 2);
printf("%d\n", n);
return 0;
}
x86
compiler
gcc -m32 -g -O0 -std=c99 -fno-stack-protector test.c
Vérifier avec gdb
salacia@Vega:~/tmp/gdb$ gdb a.out
GNU gdb (Ubuntu 7.11.1-0ubuntu1~16.5) 7.11.1
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from a.out...done.
(gdb) b func
Breakpoint 1 at 0x8048411: file test.c, line 5.
(gdb) r
Starting program: /home/salacia/tmp/gdb/a.out
Breakpoint 1, func (a=1, b=2) at test.c:5
5 c = a + b;
(gdb) p $eip
$1 = (void (*)()) 0x8048411 <func+6>
La commande qui s'est arrêtée était l'instruction placée à l'adresse «0x8048411». (Jusqu'à la commande précédente a été exécutée)
Regardons les commandes avant et après.
salacia@Vega:~/tmp/gdb$ objdump -d -M intel a.out
a.out: file format elf32-i386
=============================réduction=============================
0804840b <func>:
804840b: 55 push ebp
804840c: 89 e5 mov ebp,esp
804840e: 83 ec 10 sub esp,0x10
8048411: 8b 55 08 mov edx,DWORD PTR [ebp+0x8]
8048414: 8b 45 0c mov eax,DWORD PTR [ebp+0xc]
8048417: 01 d0 add eax,edx
8048419: 89 45 fc mov DWORD PTR [ebp-0x4],eax
804841c: 8b 45 fc mov eax,DWORD PTR [ebp-0x4]
804841f: 01 c0 add eax,eax
8048421: c9 leave
8048422: c3 ret
804840b: Enregistrer le pointeur de base 804840c: définissez le pointeur de pile sur le nouveau pointeur de base. 804840e: soustrayez esp pour réserver de l'espace pour les variables locales 8048411: Copiez l'argument dans le registre de l'opération (arrêté ici), le traitement dans la fonction à partir d'ici
Ainsi, en x86, si vous définissez un point d'arrêt sur une fonction, il s'avère que `` le traitement du frame de pile est exécuté ''.
Je n'ai pas touché l'assembleur x64, donc je ne sais pas quel sera le résultat, mais j'ai fait mes propres recherches. Si vous faites une erreur, j'apprécierais que vous la signaliez.
compiler
gcc -g -O0 -std=c99 -fno-stack-protector test.c
Confirmation avec gdb
salacia@Vega:~/tmp/gdb$ gdb a.out
GNU gdb (Ubuntu 7.11.1-0ubuntu1~16.5) 7.11.1
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from a.out...done.
(gdb) b func
Breakpoint 1 at 0x400530: file test.c, line 5.
(gdb) r
Starting program: /home/salacia/tmp/gdb/a.out
Breakpoint 1, func (a=1, b=2) at test.c:5
5 c = a + b;
(gdb) p $rip
$1 = (void (*)()) 0x400530 <func+10>
Il s'est arrêté à l'instruction à l'adresse «0x400530». Jetez un œil aux instructions avant et après cette adresse.
salacia@Vega:~/tmp/gdb$ objdump -d -M intel a.out
a.out: file format elf64-x86-64
=============================réduction=============================
0000000000400526 <func>:
400526: 55 push rbp
400527: 48 89 e5 mov rbp,rsp
40052a: 89 7d ec mov DWORD PTR [rbp-0x14],edi
40052d: 89 75 e8 mov DWORD PTR [rbp-0x18],esi
400530: 8b 55 ec mov edx,DWORD PTR [rbp-0x14]
400533: 8b 45 e8 mov eax,DWORD PTR [rbp-0x18]
400536: 01 d0 add eax,edx
400538: 89 45 fc mov DWORD PTR [rbp-0x4],eax
40053b: 8b 45 fc mov eax,DWORD PTR [rbp-0x4]
40053e: 01 c0 add eax,eax
400540: 5d pop rbp
400541: c3 ret
Tout d'abord, le pointeur de base est enregistré et un nouveau pointeur de base est défini.
Le prochain est probablement la partie du traitement des arguments.
La convention d'appel x64 de Linux (System V ABI) tente de passer des arguments dans les registres avant l'empilement.
Comme il y a deux arguments de 4 octets cette fois, l'appelant stocke les valeurs dans ʻedi et ʻesi
.
Nous avons copié ces valeurs dans le cadre de la pile (instructions «40052a» et «40052d»).
Jusqu'à présent, nous avons atteint un point d'arrêt. Contrairement à x86, il n'y a pas de soustraction de pointeur de pile, etc. Dans le cas de x64, est-ce le traitement du traitement de trame de pile? Est-ce normal de penser cela?
C'est désagréable de finir ici, alors je vais essayer un peu plus.
J'ai regardé une fonction qui ne fait rien en particulier pour savoir dans quelle mesure elle traite les cadres de pile.
salacia@Vega:~/tmp/gdb$ cat func.c
int func(int a, int b){
int c;
return 0;
}
salacia@Vega:~/tmp/gdb$ objdump -d -M intel func.o
func.o: file format elf64-x86-64
Disassembly of section .text:
0000000000000000 <func>:
0: 55 push rbp
1: 48 89 e5 mov rbp,rsp
4: 89 7d fc mov DWORD PTR [rbp-0x4],edi
7: 89 75 f8 mov DWORD PTR [rbp-0x8],esi
a: b8 00 00 00 00 mov eax,0x0
f: 5d pop rbp
10: c3 ret
Contrairement à x86, il ne semble pas soustraire le pointeur de pile. Et, après tout, l'argument est copié dans le cadre de la pile. Après cela, puisque l'emplacement de retour est entré dans ʻeax`, le traitement dans la fonction a déjà commencé.
Je pense que le push ne peut pas être effectué à moins que le pointeur de pile ne soit soustrait, mais comme il y a peu de variables locales, cela signifie-t-il que les opérations de pile sont inutiles?
Donc, cette fois, j'ai essayé d'utiliser la pile. En x64, si le nombre d'arguments est grand et qu'il ne peut pas être passé dans le registre, la pile sera utilisée, j'ai donc appelé une fonction avec de nombreux arguments.
int func2(int a, int b, int c, int d, int e, int f, int g){
return 0;
}
int func(int a, int b){
int c = 0x00;
int d = 0x11;
int e = 0x22;
int f = 0x33;
int g = 0x44;
int h = 0x55;
int i = 0x66;
func2(a, b, c, d, e, f, g);
return 0;
}
salacia@Vega:~/tmp/gdb$ objdump -d -M intel func.o
func.o: file format elf64-x86-64
Disassembly of section .text:
0000000000000000 <func2>:
0: 55 push rbp
1: 48 89 e5 mov rbp,rsp
4: 89 7d fc mov DWORD PTR [rbp-0x4],edi
7: 89 75 f8 mov DWORD PTR [rbp-0x8],esi
a: 89 55 f4 mov DWORD PTR [rbp-0xc],edx
d: 89 4d f0 mov DWORD PTR [rbp-0x10],ecx
10: 44 89 45 ec mov DWORD PTR [rbp-0x14],r8d
14: 44 89 4d e8 mov DWORD PTR [rbp-0x18],r9d
18: b8 00 00 00 00 mov eax,0x0
1d: 5d pop rbp
1e: c3 ret
000000000000001f <func>:
1f: 55 push rbp
20: 48 89 e5 mov rbp,rsp
23: 48 83 ec 28 sub rsp,0x28
27: 89 7d dc mov DWORD PTR [rbp-0x24],edi
2a: 89 75 d8 mov DWORD PTR [rbp-0x28],esi
2d: c7 45 fc 00 00 00 00 mov DWORD PTR [rbp-0x4],0x0
34: c7 45 f8 11 00 00 00 mov DWORD PTR [rbp-0x8],0x11
3b: c7 45 f4 22 00 00 00 mov DWORD PTR [rbp-0xc],0x22
42: c7 45 f0 33 00 00 00 mov DWORD PTR [rbp-0x10],0x33
49: c7 45 ec 44 00 00 00 mov DWORD PTR [rbp-0x14],0x44
50: c7 45 e8 55 00 00 00 mov DWORD PTR [rbp-0x18],0x55
57: c7 45 e4 66 00 00 00 mov DWORD PTR [rbp-0x1c],0x66
5e: 44 8b 4d f0 mov r9d,DWORD PTR [rbp-0x10]
62: 44 8b 45 f4 mov r8d,DWORD PTR [rbp-0xc]
66: 8b 4d f8 mov ecx,DWORD PTR [rbp-0x8]
69: 8b 55 fc mov edx,DWORD PTR [rbp-0x4]
6c: 8b 75 d8 mov esi,DWORD PTR [rbp-0x28]
6f: 8b 45 dc mov eax,DWORD PTR [rbp-0x24]
72: 8b 7d ec mov edi,DWORD PTR [rbp-0x14]
75: 57 push rdi
76: 89 c7 mov edi,eax
78: e8 00 00 00 00 call 7d <func+0x5e>
7d: 48 83 c4 08 add rsp,0x8
81: b8 00 00 00 00 mov eax,0x0
86: c9 leave
87: c3 ret
Lors de l'exécution d'une opération de pile dans une fonction, il semble que le pointeur de pile soit soustrait (instruction 0x23
).
Certaines fonctions ont moins d'instructions, on peut donc dire qu'elles sont plus optimisées que x86.
Alors, où s'arrête la fonction qui a commencé à utiliser cette pile lorsqu'elle est définie comme un point d'arrêt?
#include <stdio.h>
int func2(int a, int b, int c, int d, int e, int f, int g){
return 0;
}
int func(int a, int b){
int c = 0x00;
int d = 0x11;
int e = 0x22;
int f = 0x33;
int g = 0x44;
int h = 0x55;
int i = 0x66;
func2(a, b, c, d, e, f, g);
return 0;
}
int main(void){
int n;
n = func(1, 2);
printf("%d\n", n);
return 0;
}
salacia@Vega:~/tmp/gdb$ gdb a.out
GNU gdb (Ubuntu 7.11.1-0ubuntu1~16.5) 7.11.1
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from a.out...done.
(gdb) b func
Breakpoint 1 at 0x400553: file test.c, line 8.
(gdb) r
Starting program: /home/salacia/tmp/gdb/a.out
Breakpoint 1, func (a=1, b=2) at test.c:8
8 int c = 0x00;
(gdb) p $rip
$1 = (void (*)()) 0x400553 <func+14>
Regardez avant et après 0x400553
.
0000000000400545 <func>:
400545: 55 push rbp
400546: 48 89 e5 mov rbp,rsp
400549: 48 83 ec 28 sub rsp,0x28
40054d: 89 7d dc mov DWORD PTR [rbp-0x24],edi
400550: 89 75 d8 mov DWORD PTR [rbp-0x28],esi
400553: c7 45 fc 00 00 00 00 mov DWORD PTR [rbp-0x4],0x0
40055a: c7 45 f8 11 00 00 00 mov DWORD PTR [rbp-0x8],0x11
400561: c7 45 f4 22 00 00 00 mov DWORD PTR [rbp-0xc],0x22
400568: c7 45 f0 33 00 00 00 mov DWORD PTR [rbp-0x10],0x33
40056f: c7 45 ec 44 00 00 00 mov DWORD PTR [rbp-0x14],0x44
400576: c7 45 e8 55 00 00 00 mov DWORD PTR [rbp-0x18],0x55
40057d: c7 45 e4 66 00 00 00 mov DWORD PTR [rbp-0x1c],0x66
400584: 44 8b 4d f0 mov r9d,DWORD PTR [rbp-0x10]
400588: 44 8b 45 f4 mov r8d,DWORD PTR [rbp-0xc]
40058c: 8b 4d f8 mov ecx,DWORD PTR [rbp-0x8]
40058f: 8b 55 fc mov edx,DWORD PTR [rbp-0x4]
400592: 8b 75 d8 mov esi,DWORD PTR [rbp-0x28]
400595: 8b 45 dc mov eax,DWORD PTR [rbp-0x24]
400598: 8b 7d ec mov edi,DWORD PTR [rbp-0x14]
40059b: 57 push rdi
40059c: 89 c7 mov edi,eax
40059e: e8 83 ff ff ff call 400526 <func2>
4005a3: 48 83 c4 08 add rsp,0x8
4005a7: b8 00 00 00 00 mov eax,0x0
4005ac: c9 leave
4005ad: c3 ret
Il semble qu'il a cessé de s'exécuter jusqu'à ce que l'argument soit copié dans le cadre de la pile. À partir de là, le processus d'attribution de la valeur initiale à la variable locale a commencé.
Dans x64, ce qui suit est le traitement du frame de pile, et si vous définissez un point d'arrêt dans la fonction avec gdb, exécutez-le jusqu'à présent
--Enregistrer le pointeur de base et définir un nouveau pointeur de base
J'ai beaucoup appris. ~~ (Le bonus était l'histoire principale) ~~
Recommended Posts