Comment les registres fs/gs sont-ils utilisés dans Linux AMD64?
Sur l'architecture x86-64, deux registres ont un but particulier: FS et GS. Dans linux 2.6.* , le registre FS semble être utilisé pour stocker des informations thread-local.
- Est-ce exact?
- Qu'est-ce qui est stocké à fs: 0? Y a-t-il une structure C qui décrit ce contenu?
- Quelle est alors l'utilisation de GS?
3 réponses
Dans x86-64 Il y a 3 entrées TLS , deux d'entre elles accessibles via FS et GS , FS est utilisé en interne par la glibc (dans IA32 apparemment FS est utilisé par Wine et GS par la glibc).
La Glibc fait son point D'entrée TLS à un struct pthread
cela contient des structures internes pour le threading. La glibc fait généralement référence à une variable struct pthread
comme pd
, probablement pour pthread descriptor .
Sur x86-64, struct pthread
commence avec un tcbhead_t
(cela dépend l'architecture, voir les macros TLS_DTV_AT_TP
et TLS_TCB_AT_TP
). Cet en-tête de bloc de contrôle de Thread, AFAIU, contient certains champs qui sont nécessaires même lorsqu'il y a un seul thread. Le DTV est le vecteur de Thread dynamique, et contient des pointeurs vers des blocs TLS pour les DSO chargés via dlopen()
. Avant ou après le TCB, il existe un bloc TLS statique pour l'exécutable et les DSOs liés au temps de chargement (du programme). Le TCB et la DTV sont assez bien expliqués dans le document TLS d'Ulrich Drepper (regardez pour les diagrammes du chapitre 3).
Pour répondre réellement à votre question fs:0
: L'ABI x86_64 exige que fs:0
contienne l'adresse "pointée" par fs
elle-même. C'est, fs:-4
charge la valeur stockée à fs:0 - 4
. Cette fonctionnalité est nécessaire car vous ne pouvez pas facilement obtenir l'adresse pointée par fs
sans passer par le code du noyau. Avoir l'adresse stockée à fs:0
rend ainsi le travail avec le stockage local de thread beaucoup plus efficace.
Comme Vous pouvez le voir en action lorsque vous prenez l'adresse d'un thread local variable:
static __thread int test = 0;
int *f(void) {
return &test;
}
int g(void) {
return test;
}
Compile à
f:
movq %fs:0, %rax
leaq -4(%rax), %rax
retq
g:
movl %fs:-4, %eax
retq
I686 fait la même chose mais avec %gs
. Sur aarch64, ce n'est pas nécessaire car l'adresse peut être lue à partir du registre tls lui-même.
Quelle est alors l'utilisation de GS?
Le noyau Linux x86_64 utilise GS register comme un moyen efficace d'acquérir la pile d'espace du noyau pour les appels système.
GS register stocke l'adresse de base pour la zone par cpu. Pour acquérir la pile d'espace noyau, dans entry_SYSCALL_64
movq PER_CPU_VAR(cpu_current_top_of_stack), %rsp
Après avoir développé PER_CPU_VAR, nous obtenons ce qui suit:
movq %gs:cpu_current_top_of_stack, %rsp