¿Cómo se usan los registros fs / gs en Linux AMD64?

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.

  • ¿Es eso correcto?
  • ¿Qué se almacena en fs: 0? ¿Hay alguna estructura C que describa este contenido?
  • ¿Cuál es entonces el uso de GS?

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.