Как регистры fs/gs используются в Linux AMD64?
В архитектуре x86-64 специальное назначение имеют два регистра: FS и GS. В linux 2.6.* Регистр FS, похоже, используется для хранения локальной информации о потоках.
- Это верно?
- Что хранится в фс:0? Есть ли структура C, которая описывает это содержание?
- Какая тогда польза от ГС?
3 ответа
В x86-64 имеется 3 записи TLS, две из них доступны через FS и GS, FS используется внутренне для glibc (в IA32 очевидно FS используется Wine и GS для glibc).
Glibc делает свою точку входа TLS в struct pthread
который содержит некоторые внутренние структуры для работы с потоками. Глибц обычно относится к struct pthread
переменная как pd
предположительно для дескриптора pthread.
На x86-64, struct pthread
начинается с tcbhead_t
(это зависит от архитектуры, см. макросы TLS_DTV_AT_TP
а также TLS_TCB_AT_TP
). Этот заголовок блока управления потоками, AFAIU, содержит некоторые поля, которые необходимы, даже если существует один поток. DTV является вектором динамического потока и содержит указатели на блоки TLS для DSO, загружаемых через dlopen()
, До или после TCB существует статический блок TLS для исполняемого файла и DSO, связанных во время загрузки (программы). TCB и DTV довольно хорошо объяснены в документе TLS Ульриха Дреппера (см. Схемы в главе 3).
На самом деле ответить на ваш fs:0
вопрос: x86_64 ABI требует, чтобы fs:0
содержит адрес, на который указывает fs
сам. То есть, fs:-4
загружает значение, хранящееся в fs:0 - 4
, Эта функция необходима, потому что вы не можете легко получить адрес, на который указывает fs
без прохождения кода ядра. Хранение адреса в fs:0
таким образом, работа с локальным хранилищем потоков становится намного более эффективной.
Это можно увидеть в действии, когда вы берете адрес локальной переменной потока:
static __thread int test = 0;
int *f(void) {
return &test;
}
int g(void) {
return test;
}
компилируется в
f:
movq %fs:0, %rax
leaq -4(%rax), %rax
retq
g:
movl %fs:-4, %eax
retq
i686 делает то же самое, но с %gs
, На aarch64 это не нужно, потому что адрес может быть считан из самого регистра tls.
Какая тогда польза от ГС?
Ядро Linux x86_64 использует регистр GS как эффективный способ получения стека пространства ядра для системных вызовов.
Регистр GS хранит базовый адрес для области процессора. Чтобы получить стек пространства ядра, в entry_SYSCALL_64
movq PER_CPU_VAR(cpu_current_top_of_stack), %rsp
После расширения PER_CPU_VAR мы получаем следующее:
movq %gs:cpu_current_top_of_stack, %rsp