En la architecture x86-64, dos registros tienen un propósito especial: FS y GS. En Linux 2.6. *, El registro FS parece usarse para almacenar información local de subprocesos.
En x86-64 hay 3 entradas TLS , dos de ellas accesibles vía FS y GS , FS se usa internamente por glibc (en IA32 aparentemente FS es usado por Wine y GS por glibc ).
Glibc hace que su punto de entrada TLS sea un struct pthread
que contiene algunas estructuras internas para enhebrar. Glibc generalmente se refiere a una variable struct pthread
como pd
, presumiblemente para el descriptor pthread .
En x86-64, struct pthread
comienza con un tcbhead_t
(esto depende de la architecture, vea las macros TLS_DTV_AT_TP
y TLS_TCB_AT_TP
). Este encabezado del bloque de control de subprocesos, AFAIU, contiene algunos campos que se necesitan incluso cuando hay un solo subproceso. El DTV es el Dynamic Thread Vector y contiene punteros a bloques TLS para DSO cargados a través de dlopen()
. Antes o después del TCB hay un bloque estático TLS para el ejecutable y DSO vinculados al tiempo de carga (del progtwig). El TCB y DTV se explican bastante bien en el documento TLS de Ulrich Drepper (busque los diagtwigs en el capítulo 3).
Para responder realmente a su pregunta fs:0
: La ABI x86_64 requiere que fs:0
contenga la dirección “apuntada” por fs
. Es decir, fs:-4
carga el valor almacenado en fs:0 - 4
. Esta característica es necesaria porque no puede obtener fácilmente la dirección apuntada por fs
sin pasar por el código del kernel. Tener la dirección almacenada en fs:0
hace que trabajar con el almacenamiento local de subprocesos sea mucho más eficiente.
Puedes ver esto en acción cuando tomas la dirección de una variable local de subprocesos:
static __thread int test = 0; int *f(void) { return &test; } int g(void) { return test; }
comstack a
f: movq %fs:0, %rax leaq -4(%rax), %rax retq g: movl %fs:-4, %eax retq
i686 hace lo mismo pero con %gs
. En aarch64 esto no es necesario porque la dirección se puede leer desde el propio registro tls.