Как регистры 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
Другие вопросы по тегам