Es ist sehr einfach, aber ich wusste es bisher nicht, also notieren Sie es sich.
Als ich einen Haltepunkt für eine Funktion in GDB festlegte, wusste ich nicht, wo ich ihn auf Maschinensprachenebene stoppen sollte. Unmittelbar vor dem Anruf des Anrufers? Unmittelbar nach dem Anruf? Nach dem Drücken des Basiszeigers? Nach dem Sichern des Bereichs für lokale Variablen?
Aus der Schlussfolgerung ging hervor, dass "nach Zuweisung des Bereichs der lokalen Variablen". (Das heißt, die minimal erforderliche Stapelrahmenverarbeitung wird beim Aufruf ausgeführt.)
Die Sprache (Standard) ist C99.
Umgebung
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
Bestätigungscode
#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
kompilieren
gcc -m32 -g -O0 -std=c99 -fno-stack-protector test.c
Fragen Sie bei gdb nach
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>
Der Befehl, der gestoppt wurde, war die Anweisung an der Adresse "0x8048411". (Bis zum vorherigen Befehl wurde ausgeführt)
Schauen wir uns die Befehle vorher und nachher an.
salacia@Vega:~/tmp/gdb$ objdump -d -M intel a.out
a.out: file format elf32-i386
=============================Kürzung=============================
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: Basiszeiger speichern 804840c: Setzen Sie den Stapelzeiger auf den neuen Basiszeiger. 804840e: Subtrahiere esp, um Platz für lokale Variablen zu reservieren 8048411: Kopieren Sie das Argument in das Register für die Operation (hier gestoppt), die Verarbeitung in der Funktion von hier
Wenn Sie also in x86 einen Haltepunkt für eine Funktion festlegen, stellt sich heraus, dass "die Verarbeitung des Stapelrahmens ausgeführt wird".
Ich habe den x64-Assembler nicht berührt, daher weiß ich nicht, wie das Ergebnis aussehen wird, aber ich habe meine eigenen Nachforschungen angestellt. Wenn Sie einen Fehler machen, würde ich es begrüßen, wenn Sie darauf hinweisen könnten.
kompilieren
gcc -g -O0 -std=c99 -fno-stack-protector test.c
Bestätigung mit 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>
Es stoppte bei der Anweisung unter der Adresse "0x400530". Schauen wir uns die Anweisungen vor und nach dieser Adresse an.
salacia@Vega:~/tmp/gdb$ objdump -d -M intel a.out
a.out: file format elf64-x86-64
=============================Kürzung=============================
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
Zunächst wird der Basiszeiger gespeichert und ein neuer Basiszeiger gesetzt.
Als nächstes folgt wahrscheinlich der Teil der Verarbeitung der Argumente.
Die x64-Aufrufkonvention von Linux (System V ABI) versucht, Argumente vor dem Stapeln in Registern zu übergeben.
Da es diesmal zwei 4-Byte-Argumente gibt, speichert der Aufrufer die Werte in "edi" und "esi".
Wir haben diese Werte in den Stapelrahmen kopiert (Anweisungen 40052a
und 40052d
).
Bisher haben wir einen Haltepunkt erreicht. Im Gegensatz zu x86 gibt es keine Stapelzeigersubtraktion usw. Ist dies im Fall von x64 die Behandlung der Stapelrahmenverarbeitung? Ist es in Ordnung, das zu denken?
Es ist unangenehm, hier zu enden, also werde ich ein bisschen mehr versuchen.
Ich habe mir eine Funktion angesehen, die nichts Besonderes tut, um herauszufinden, wie weit sie Stapelrahmen verarbeitet.
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
Im Gegensatz zu x86 scheint es den Stapelzeiger nicht zu subtrahieren. Und schließlich wird das Argument in den Stapelrahmen kopiert. Da sich der Rückgabeort danach in "eax" befindet, befindet er sich bereits in der Verarbeitung in der Funktion.
Ich bin der Meinung, dass Push nur ausgeführt werden kann, wenn der Stapelzeiger subtrahiert wird. Bedeutet dies jedoch, dass Stapeloperationen nicht erforderlich sind, da nur wenige lokale Variablen vorhanden sind?
Also habe ich diesmal versucht, den Stack zu benutzen. Wenn in x64 die Anzahl der Argumente groß ist und nicht im Register übergeben werden kann, wird der Stapel verwendet. Daher habe ich eine Funktion mit vielen Argumenten aufgerufen.
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
Wenn eine Stapeloperation in einer Funktion ausgeführt wird, scheint der Stapelzeiger subtrahiert zu sein (Anweisung "0x23"). Einige Funktionen haben weniger Anweisungen, daher kann gesagt werden, dass sie optimierter sind als x86.
Wo hört die Funktion, die diesen Stapel verwendet hat, auf, wenn sie als Haltepunkt festgelegt wird?
#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>
Suchen Sie vor und nach 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
Es scheint, dass die Ausführung gestoppt wurde, bis das Argument in den Stapelrahmen kopiert wurde. Von hier aus hat der Prozess der Zuweisung des Anfangswertes zur lokalen Variablen begonnen.
In x64 wird der Stapelrahmen wie folgt verarbeitet. Wenn Sie mit gdb einen Haltepunkt in der Funktion festlegen, führen Sie ihn bisher aus
Ich habe viel gelernt. ~~ (Der Bonus war die Hauptgeschichte) ~~
Recommended Posts