[LINUX] Où dois-je m'arrêter lorsque je définis un point d'arrêt dans une fonction dans GDB (x86)

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é)

Contrôle de fonctionnement réel

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é ''.

Bonus (x64)

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

Où dois-je m'arrêter lorsque je définis un point d'arrêt dans une fonction dans GDB (x86)
J'obtiens un attribut impossible à définir lors de l'utilisation de @property en python
Précautions lors du décapage d'une fonction en python
Quand j'obtiens une erreur de pilote chrome dans Selenium
J'obtiens une exception java.util.regex.PatternSyntaxException lors du fractionnement d'une chaîne dans PySpark
Je veux faire quelque chose avec Python à la fin
Une histoire à laquelle j'étais accro en spécifiant nil comme argument de fonction dans Go
Un ensemble de fichiers de script qui font wordcloud avec Python3
Créer une fonction en Python
[Python] Temps d'exécution lorsqu'une fonction est saisie dans une valeur de dictionnaire
J'ai écrit une fonction pour charger le script d'extension Git en Python