Jaké je tedy použití GS?
x86_64 Linuxové jádro používá registr GS jako efektivní způsob získání zásobníku prostoru jádra pro systémová volání.
Registr GS ukládá základní adresu pro oblast na procesor. Chcete-li získat zásobník prostoru jádra, v entry_SYSCALL_64
movq PER_CPU_VAR(cpu_current_top_of_stack), %rsp
Po rozbalení PER_CPU_VAR dostaneme následující:
movq %gs:cpu_current_top_of_stack, %rsp
Chcete-li skutečně odpovědět na váš fs:0
otázka:x86_64 ABI vyžaduje fs:0
obsahuje adresu, na kterou ukazuje fs
sám. To znamená fs:-4
načte hodnotu uloženou v fs:0 - 4
. Tato funkce je nezbytná, protože nemůžete snadno získat adresu, na kterou ukazuje fs
bez procházení kódu jádra. S adresou uloženou na fs:0
díky tomu je práce s místním úložištěm vláken mnohem efektivnější.
Můžete to vidět v akci, když vezmete adresu lokální proměnné vlákna:
static __thread int test = 0;
int *f(void) {
return &test;
}
int g(void) {
return test;
}
zkompiluje do
f:
movq %fs:0, %rax
leaq -4(%rax), %rax
retq
g:
movl %fs:-4, %eax
retq
i686 dělá totéž, ale s %gs
. Na aarch64 to není nutné, protože adresu lze číst ze samotného registru tls.
V x86-64 jsou 3 záznamy TLS, dva z nich jsou přístupné přes FS a GS, FS používá interně glibc (v IA32 zřejmě FS používá Wine a GS glibc).
Glibc vytvoří svůj vstupní bod TLS na struct pthread
který obsahuje některé vnitřní struktury pro závitování. Glibc obvykle odkazuje na struct pthread
proměnná jako pd
, pravděpodobně pro deskriptor pthread .
Na x86-64, struct pthread
začíná tcbhead_t
(to záleží na architektuře, viz makra TLS_DTV_AT_TP
a TLS_TCB_AT_TP
). Toto záhlaví řídicího bloku vlákna, AFAIU, obsahuje některá pole, která jsou potřebná, i když existuje jediné vlákno. DTV je vektor dynamického vlákna a obsahuje ukazatele na bloky TLS pro DSO načtené přes dlopen()
. Před nebo za TCB je statický blok TLS pro spustitelný soubor a DSO spojené v době načítání (programu). TCB a DTV jsou docela dobře vysvětleny v dokumentu TLS Ulricha Dreppera (viz diagramy v kapitole 3).