A quoi sert alors GS ?
x86_64 Le noyau Linux utilise le registre GS comme moyen efficace d'acquérir la pile d'espace du noyau pour les appels système.
Le registre GS stocke l'adresse de base pour la zone par processeur. Pour acquérir la pile d'espace du 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
Pour répondre réellement à votre fs:0
question :L'ABI x86_64 nécessite que fs:0
contient l'adresse "pointée" par fs
lui-même. C'est-à-dire 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 des threads beaucoup plus efficace.
Vous pouvez le voir en action lorsque vous prenez l'adresse d'une variable locale de thread :
static __thread int test = 0;
int *f(void) {
return &test;
}
int g(void) {
return test;
}
compile vers
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.
Dans x86-64, il y a 3 entrées TLS, dont deux 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).
Glibc fait de son point d'entrée TLS un struct pthread
qui contient des structures internes pour le filetage. Glibc fait généralement référence à un struct pthread
variable comme pd
, vraisemblablement pour descripteur pthread .
Sur x86-64, struct pthread
commence par un tcbhead_t
(cela dépend de 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 n'y a qu'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 y a un bloc TLS statique pour l'exécutable et les DSO liés au moment du chargement (du programme). Le TCB et le DTV sont assez bien expliqués dans le document TLS d'Ulrich Drepper (recherchez les schémas au chapitre 3).