Voici le tableau complet des registres et leur utilisation à partir de la documentation [Lien PDF] :
r12
, r13
, r14
, r15
, rbx
, rsp
, rbp
sont les registres enregistrés par l'appelé - ils ont un "Oui" dans la colonne "Conservé à travers les appels de fonction".
Approche expérimentale :désassembler le code GCC
Principalement pour le plaisir, mais aussi pour vérifier rapidement que vous avez bien compris l'ABI.
Essayons d'écraser tous les registres avec un assemblage en ligne pour forcer GCC à les sauvegarder et à les restaurer :
main.c
#include <inttypes.h>
uint64_t inc(uint64_t i) {
__asm__ __volatile__(
""
: "+m" (i)
:
: "rax",
"rbx",
"rcx",
"rdx",
"rsi",
"rdi",
"rbp",
"rsp",
"r8",
"r9",
"r10",
"r11",
"r12",
"r13",
"r14",
"r15",
"ymm0",
"ymm1",
"ymm2",
"ymm3",
"ymm4",
"ymm5",
"ymm6",
"ymm7",
"ymm8",
"ymm9",
"ymm10",
"ymm11",
"ymm12",
"ymm13",
"ymm14",
"ymm15"
);
return i + 1;
}
int main(int argc, char **argv) {
(void)argv;
return inc(argc);
}
GitHub en amont.
Compiler et désassembler :
gcc -std=gnu99 -O3 -ggdb3 -Wall -Wextra -pedantic -o main.out main.c
objdump -d main.out
Le démontage contient :
00000000000011a0 <inc>:
11a0: 55 push %rbp
11a1: 48 89 e5 mov %rsp,%rbp
11a4: 41 57 push %r15
11a6: 41 56 push %r14
11a8: 41 55 push %r13
11aa: 41 54 push %r12
11ac: 53 push %rbx
11ad: 48 83 ec 08 sub $0x8,%rsp
11b1: 48 89 7d d0 mov %rdi,-0x30(%rbp)
11b5: 48 8b 45 d0 mov -0x30(%rbp),%rax
11b9: 48 8d 65 d8 lea -0x28(%rbp),%rsp
11bd: 5b pop %rbx
11be: 41 5c pop %r12
11c0: 48 83 c0 01 add $0x1,%rax
11c4: 41 5d pop %r13
11c6: 41 5e pop %r14
11c8: 41 5f pop %r15
11ca: 5d pop %rbp
11cb: c3 retq
11cc: 0f 1f 40 00 nopl 0x0(%rax)
et nous voyons donc clairement que les éléments suivants sont poussés et sautés :
rbx
r12
r13
r14
r15
rbp
Le seul manquant dans la spécification est rsp
, mais nous nous attendons à ce que la pile soit restaurée bien sûr. Une lecture attentive du montage confirme qu'il est maintenu dans ce cas :
sub $0x8, %rsp
:alloue 8 octets sur la pile pour enregistrer%rdi
à%rdi, -0x30(%rbp)
, ce qui est fait pour l'assembly en ligne+m
contraintelea -0x28(%rbp), %rsp
restaure%rsp
avant lesub
, c'est-à-dire 5 pops aprèsmov %rsp, %rbp
- il y a 6 pushs et 6 pops correspondants
- aucune autre instruction ne touche
%rsp
Testé dans Ubuntu 18.10, GCC 8.2.0.