요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.
1. 요약·해설
원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.
2. 영어 원문 전체
번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.
원문 전체 펼치기
.. SPDX-License-Identifier: GPL-2.0
Using FS and GS segments in user space applications
===================================================
The x86 architecture supports segmentation. Instructions which access
memory can use segment register based addressing mode. The following
notation is used to address a byte within a segment:
Segment-register:Byte-address
The segment base address is added to the Byte-address to compute the
resulting virtual address which is accessed. This allows to access multiple
instances of data with the identical Byte-address, i.e. the same code. The
selection of a particular instance is purely based on the base-address in
the segment register.
In 32-bit mode the CPU provides 6 segments, which also support segment
limits. The limits can be used to enforce address space protections.
In 64-bit mode the CS/SS/DS/ES segments are ignored and the base address is
always 0 to provide a full 64bit address space. The FS and GS segments are
still functional in 64-bit mode.
Common FS and GS usage
------------------------------
The FS segment is commonly used to address Thread Local Storage (TLS). FS
is usually managed by runtime code or a threading library. Variables
declared with the '__thread' storage class specifier are instantiated per
thread and the compiler emits the FS: address prefix for accesses to these
variables. Each thread has its own FS base address so common code can be
used without complex address offset calculations to access the per thread
instances. Applications should not use FS for other purposes when they use
runtimes or threading libraries which manage the per thread FS.
The GS segment has no common use and can be used freely by
applications. GCC and Clang support GS based addressing via address space
identifiers.
Reading and writing the FS/GS base address
------------------------------------------
There exist two mechanisms to read and write the FS/GS base address:
- the arch_prctl() system call
- the FSGSBASE instruction family
Accessing FS/GS base with arch_prctl()
--------------------------------------
The arch_prctl(2) based mechanism is available on all 64-bit CPUs and all
kernel versions.
Reading the base:
arch_prctl(ARCH_GET_FS, &fsbase);
arch_prctl(ARCH_GET_GS, &gsbase);
Writing the base:
arch_prctl(ARCH_SET_FS, fsbase);
arch_prctl(ARCH_SET_GS, gsbase);
The ARCH_SET_GS prctl may be disabled depending on kernel configuration
and security settings.
Accessing FS/GS base with the FSGSBASE instructions
---------------------------------------------------
With the Ivy Bridge CPU generation Intel introduced a new set of
instructions to access the FS and GS base registers directly from user
space. These instructions are also supported on AMD Family 17H CPUs. The
following instructions are available:
=============== ===========================
RDFSBASE %reg Read the FS base register
RDGSBASE %reg Read the GS base register
WRFSBASE %reg Write the FS base register
WRGSBASE %reg Write the GS base register
=============== ===========================
The instructions avoid the overhead of the arch_prctl() syscall and allow
more flexible usage of the FS/GS addressing modes in user space
applications. This does not prevent conflicts between threading libraries
and runtimes which utilize FS and applications which want to use it for
their own purpose.
FSGSBASE instructions enablement
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The instructions are enumerated in CPUID leaf 7, bit 0 of EBX. If
available /proc/cpuinfo shows 'fsgsbase' in the flag entry of the CPUs.
The availability of the instructions does not enable them
automatically. The kernel has to enable them explicitly in CR4. The
reason for this is that older kernels make assumptions about the values in
the GS register and enforce them when GS base is set via
arch_prctl(). Allowing user space to write arbitrary values to GS base
would violate these assumptions and cause malfunction.
On kernels which do not enable FSGSBASE the execution of the FSGSBASE
instructions will fault with a #UD exception.
The kernel provides reliable information about the enabled state in the
ELF AUX vector. If the HWCAP2_FSGSBASE bit is set in the AUX vector, the
kernel has FSGSBASE instructions enabled and applications can use them.
The following code example shows how this detection works::
#include <sys/auxv.h>
#include <elf.h>
/* Will be eventually in asm/hwcap.h */
#ifndef HWCAP2_FSGSBASE
#define HWCAP2_FSGSBASE (1 << 1)
#endif
....
unsigned val = getauxval(AT_HWCAP2);
if (val & HWCAP2_FSGSBASE)
printf("FSGSBASE enabled\n");
FSGSBASE instructions compiler support
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
GCC version 4.6.4 and newer provide intrinsics for the FSGSBASE
instructions. Clang 5 supports them as well.
=================== ===========================
_readfsbase_u64() Read the FS base register
_readgsbase_u64() Read the GS base register
_writefsbase_u64() Write the FS base register
_writegsbase_u64() Write the GS base register
=================== ===========================
To utilize these intrinsics <immintrin.h> must be included in the source
code and the compiler option -mfsgsbase has to be added.
Compiler support for FS/GS based addressing
-------------------------------------------
GCC version 6 and newer provide support for FS/GS based addressing via
Named Address Spaces. GCC implements the following address space
identifiers for x86:
========= ====================================
__seg_fs Variable is addressed relative to FS
__seg_gs Variable is addressed relative to GS
========= ====================================
The preprocessor symbols __SEG_FS and __SEG_GS are defined when these
address spaces are supported. Code which implements fallback modes should
check whether these symbols are defined. Usage example::
#ifdef __SEG_GS
long data0 = 0;
long data1 = 1;
long __seg_gs *ptr;
/* Check whether FSGSBASE is enabled by the kernel (HWCAP2_FSGSBASE) */
....
/* Set GS base to point to data0 */
_writegsbase_u64(&data0);
/* Access offset 0 of GS */
ptr = 0;
printf("data0 = %ld\n", *ptr);
/* Set GS base to point to data1 */
_writegsbase_u64(&data1);
/* ptr still addresses offset 0! */
printf("data1 = %ld\n", *ptr);
Clang does not provide the GCC address space identifiers, but it provides
address spaces via an attribute based mechanism in Clang 2.6 and newer
versions:
==================================== =====================================
__attribute__((address_space(256)) Variable is addressed relative to GS
__attribute__((address_space(257)) Variable is addressed relative to FS
==================================== =====================================
FS/GS based addressing with inline assembly
-------------------------------------------
In case the compiler does not support address spaces, inline assembly can
be used for FS/GS based addressing mode::
mov %fs:offset, %reg
mov %gs:offset, %reg
mov %reg, %fs:offset
mov %reg, %gs:offset
3. 한국어 전문 번역
영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.
x86 segmentation과 FS/GS
1-24이 문서는 `SPDX-License-Identifier: GPL-2.0`으로 배포됩니다. x86 architecture는 segmentation을 지원하며 memory-access instruction은 segment register 기반 addressing mode를 사용할 수 있습니다. segment 안의 byte는 다음 notation으로 address합니다: `Segment-register:Byte-address`.
segment base address를 Byte-address에 더해 access할 virtual address를 계산합니다. 따라서 같은 Byte-address와 동일한 code로 data instance 여러 개에 access할 수 있고, 어느 instance를 선택할지는 segment register의 base address만으로 결정됩니다.
32-bit mode에서 CPU는 여섯 segment를 제공하며 address-space protection에 사용할 수 있는 segment limit도 지원합니다.
64-bit mode에서는 full 64-bit address space를 제공하기 위해 CS/SS/DS/ES segment를 무시하고 base address를 항상 0으로 둡니다. FS와 GS segment는 64-bit mode에서도 계속 동작합니다.
FS의 TLS와 GS addressing
25-40FS segment는 일반적으로 Thread Local Storage (TLS)를 address하는 데 사용하며 runtime code나 threading library가 관리합니다. `__thread` storage-class specifier로 선언한 variable은 thread마다 instance가 생기고 compiler가 access에 `FS:` address prefix를 생성합니다.
각 thread가 자체 FS base address를 가지므로 common code가 복잡한 address-offset 계산 없이 per-thread instance에 access할 수 있습니다. per-thread FS를 관리하는 runtime이나 threading library를 사용한다면 application은 FS를 다른 목적으로 사용하면 안 됩니다.
GS segment에는 보편적인 용도가 없어 application이 자유롭게 사용할 수 있습니다. GCC와 Clang은 address-space identifier로 GS-based addressing을 지원합니다.
arch_prctl()로 FS/GS base 접근
41-68FS/GS base address를 읽고 쓰는 mechanism은 두 가지입니다.
- `arch_prctl()` system call
- FSGSBASE instruction family
`arch_prctl(2)` 기반 mechanism은 모든 64-bit CPU와 모든 kernel version에서 사용할 수 있습니다.
base를 읽는 call은 다음과 같습니다.
arch_prctl(ARCH_GET_FS, &fsbase);
arch_prctl(ARCH_GET_GS, &gsbase);
base를 쓰는 call은 다음과 같습니다.
arch_prctl(ARCH_SET_FS, fsbase);
arch_prctl(ARCH_SET_GS, gsbase);
`ARCH_SET_GS` prctl은 kernel configuration과 security setting에 따라 비활성화될 수 있습니다.
FSGSBASE instruction family
69-89Intel은 Ivy Bridge CPU generation에서 userspace가 FS와 GS base register에 직접 access하는 새 instruction set을 도입했습니다. AMD Family 17H CPU도 이를 지원합니다.
| instruction | 동작 |
|---|---|
| `RDFSBASE %reg` | FS base register를 읽습니다. |
| `RDGSBASE %reg` | GS base register를 읽습니다. |
| `WRFSBASE %reg` | FS base register를 씁니다. |
| `WRGSBASE %reg` | GS base register를 씁니다. |
이 instruction은 `arch_prctl()` syscall overhead를 피하고 userspace application이 FS/GS addressing mode를 더 유연하게 사용하도록 합니다. 다만 FS를 사용하는 threading library·runtime과 FS를 자체 용도로 쓰려는 application 사이의 conflict까지 막지는 않습니다.
FSGSBASE enablement 감지
90-124FSGSBASE instruction은 CPUID leaf 7의 `EBX` bit 0에 enumerate됩니다. 사용할 수 있으면 `/proc/cpuinfo`의 CPU flag entry에 `fsgsbase`가 표시됩니다.
instruction availability만으로 자동 활성화되지는 않으며 kernel이 `CR4`에서 명시적으로 enable해야 합니다. 구형 kernel은 GS register value에 대해 가정하고 `arch_prctl()`로 GS base를 설정할 때 이를 강제합니다. userspace의 arbitrary GS base write를 허용하면 이 가정이 깨져 malfunction할 수 있기 때문입니다.
FSGSBASE를 enable하지 않은 kernel에서 해당 instruction을 실행하면 `#UD` exception으로 fault합니다.
kernel은 ELF AUX vector에 신뢰할 수 있는 enable 상태를 제공합니다. AUX vector에 `HWCAP2_FSGSBASE` bit가 설정되어 있으면 kernel이 FSGSBASE instruction을 enable한 것이므로 application이 사용할 수 있습니다. 감지 예제는 다음과 같습니다.
#include <sys/auxv.h>
#include <elf.h>
/* Will be eventually in asm/hwcap.h */
#ifndef HWCAP2_FSGSBASE
#define HWCAP2_FSGSBASE (1 << 1)
#endif
....
unsigned val = getauxval(AT_HWCAP2);
if (val & HWCAP2_FSGSBASE)
printf("FSGSBASE enabled\n");
compiler intrinsic 지원
125-140GCC 4.6.4 이상과 Clang 5는 FSGSBASE instruction intrinsic을 지원합니다.
| intrinsic | 동작 |
|---|---|
| `_readfsbase_u64()` | FS base register를 읽습니다. |
| `_readgsbase_u64()` | GS base register를 읽습니다. |
| `_writefsbase_u64()` | FS base register를 씁니다. |
| `_writegsbase_u64()` | GS base register를 씁니다. |
이 intrinsic을 사용하려면 source code에 `<immintrin.h>`를 include하고 compiler option `-mfsgsbase`를 추가해야 합니다.
GCC named address space
141-179GCC 6 이상은 Named Address Spaces로 FS/GS-based addressing을 지원합니다. x86용 address-space identifier는 다음과 같습니다.
| identifier | address 기준 |
|---|---|
| `__seg_fs` | variable을 FS relative로 address합니다. |
| `__seg_gs` | variable을 GS relative로 address합니다. |
이 address space를 지원하면 preprocessor symbol `__SEG_FS`와 `__SEG_GS`를 정의합니다. fallback mode를 구현하는 code는 symbol 정의 여부를 확인해야 합니다.
#ifdef __SEG_GS
long data0 = 0;
long data1 = 1;
long __seg_gs *ptr;
/* Check whether FSGSBASE is enabled by the kernel (HWCAP2_FSGSBASE) */
....
/* Set GS base to point to data0 */
_writegsbase_u64(&data0);
/* Access offset 0 of GS */
ptr = 0;
printf("data0 = %ld\n", *ptr);
/* Set GS base to point to data1 */
_writegsbase_u64(&data1);
/* ptr still addresses offset 0! */
printf("data1 = %ld\n", *ptr);
예제는 GS base를 `data0`에서 `data1`으로 바꿔도 `ptr`이 계속 GS offset 0을 나타내므로 같은 code가 서로 다른 data instance를 읽는 방식을 보여 줍니다.
Clang address-space attribute
180-188Clang은 GCC address-space identifier를 제공하지 않지만 Clang 2.6 이상에서 attribute 기반 address space를 제공합니다.
| attribute | address 기준 |
|---|---|
| `__attribute__((address_space(256)))` | variable을 GS relative로 address합니다. |
| `__attribute__((address_space(257)))` | variable을 FS relative로 address합니다. |
inline assembly fallback
189-199compiler가 address space를 지원하지 않으면 inline assembly로 FS/GS-based addressing mode를 사용할 수 있습니다.
mov %fs:offset, %reg
mov %gs:offset, %reg
mov %reg, %fs:offset
mov %reg, %gs:offset
요약과 해설
fsgs.rst:1-19964-bit mode에서도 FS와 GS는 base-relative addressing을 제공하며 FS는 주로 TLS에, GS는 application 전용 data instance에 사용합니다.
`arch_prctl()`은 보편적인 interface이고 FSGSBASE instruction은 syscall 없이 base를 다룹니다. 사용 전 `HWCAP2_FSGSBASE`를 확인하고 GCC/Clang의 intrinsic·address-space 지원 또는 inline assembly를 선택해야 합니다.