Как регистры fs/gs используются в Linux AMD64?

В архитектуре x86-64 два регистра имеют особое назначение: FS и GS. В linux 2.6. * Регистр FS используется для хранения информации о локальных потоках.

  • Правильно ли это?
  • Что хранится в fs: 0? Существует ли структура C, описывающая этот контент?
  • Что такое использование GS?

Ответ 1

В x86-64 есть 3 записи TLS, два из них доступны через FS и GS, FS используется внутренне с помощью glibc (в IA32, по-видимому, FS используется Wine и GS glibc).

Glibc делает точку входа TLS в struct pthread, которая содержит некоторые внутренние структуры для потоковой передачи. Glibc обычно ссылается на переменную 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 хорошо объясняются в документе Ulrich Drepper TLS (ищите диаграммы в главе 3).

Ответ 2

Чтобы ответить на ваш вопрос fs:0: для AB86 x86_64 требуется, чтобы 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.