Article by Ayman Alheraki on January 11 2026 10:37 AM
Segment registers in the x86-64 architecture are legacy components inherited from the earlier x86 designs, originally created to support segmented memory models. Although the introduction of 64-bit mode significantly reduced their role, segment registers remain relevant for compatibility, certain operating modes, and specific system-level functions.
In x86-64 architecture, the segment registers primarily serve as selectors for segment descriptors stored in the Global Descriptor Table (GDT) or Local Descriptor Table (LDT). These descriptors define segment base addresses, limits, and access rights.
There are six segment registers in x86-64:
CS (Code Segment)
DS (Data Segment)
SS (Stack Segment)
ES (Extra Segment)
FS (General-purpose Segment)
GS (General-purpose Segment)
The CS register points to the segment containing executable code. It plays a critical role in determining the base address for instruction fetching. In 64-bit mode, CS base is generally treated as zero, and segmentation for code segments is largely disabled, allowing a flat memory model for code execution. However, CS remains essential for defining the current privilege level (CPL), influencing protection and privilege checks.
DS (Data Segment): Traditionally used as the default segment for most data accesses. In 64-bit mode, the base is typically ignored, and a flat memory model applies. However, DS is still used in legacy and compatibility modes.
ES (Extra Segment): Originally used to provide an additional data segment, particularly for string operations. Like DS, it has minimal practical segmentation impact in 64-bit mode but is retained for compatibility and certain system instructions.
SS (Stack Segment): Specifies the segment for stack operations. Stack instructions implicitly use SS. While segmentation is mostly disabled in 64-bit mode, SS controls privilege levels and is relevant during mode switches and system events.
FS and GS have become the most significant segment registers in x86-64 due to their support for base addressing in 64-bit mode. Unlike other segment registers, FS and GS can hold non-zero base addresses, allowing efficient access to thread-local storage (TLS), processor-specific data, and other per-thread or per-CPU information.
The base addresses for FS and GS are stored in model-specific registers (MSRs) and can be modified by privileged instructions (WRMSR) or operating system services. This capability makes FS and GS indispensable in modern OS kernels, threading libraries, and runtime environments.
Segment registers retain their importance for running legacy 16-bit and 32-bit applications in compatibility or virtual 8086 modes. Assemblers must support the segment prefix override functionality, which allows instructions to specify alternative segment registers for memory accesses.
Modern 64-bit applications leverage FS and GS for position-independent code and thread-local storage mechanisms:
Thread-Local Storage (TLS): FS and GS bases point to thread-specific data structures, allowing threads to efficiently access their private data without costly lookups.
Operating System Kernel: Operating systems like Windows and Linux use GS or FS to reference CPU or process control blocks, enabling fast context switches and interrupt handling.
While segment registers cannot be used as general-purpose registers, several instructions interact directly with them:
Segment Override Prefixes: In instruction encoding, segment override prefixes allow modifying the default segment used in a memory operand, enabling access to data through ES, CS, SS, DS, FS, or GS explicitly.
Load Segment Registers: The MOV instruction can load segment selectors into segment registers, for example:
MOV DS, AXMOV ES, AXThese instructions load the segment selector from a general-purpose register.
Far Jumps, Calls, and Returns: In 16/32-bit modes, far jumps, calls, and returns modify both CS and RIP/EIP. Although far jumps are rare in 64-bit mode, assemblers must still support encoding these for legacy modes.
Loading FS and GS Base: Privileged instructions (WRFSBASE, WRGSBASE, introduced with newer CPUs) and system calls set or read the base addresses of FS and GS. These instructions are essential for low-level system programming and need explicit assembler support.
When designing a custom x86-64 assembler, segment registers require special attention in the following areas:
Supporting Segment Override Prefixes: The assembler must parse and encode segment override prefixes correctly to alter default segment usage in memory operands.
Encoding Segment Register Loads: Proper instruction encoding for moves to/from segment registers is necessary, especially for system-level or legacy code.
Handling FS/GS Base Instructions: Modern processors provide specific instructions to read and write FS and GS base registers; the assembler should support these to facilitate thread-local storage and OS-level tasks.
Compatibility Modes: The assembler must be capable of generating instructions for 16-bit, 32-bit, and 64-bit modes, adjusting the semantics and usage of segment registers accordingly.
Relocation and Linking: Since segment registers refer to selectors rather than absolute addresses, the assembler and linker must cooperate to resolve segment selectors correctly for protected mode and long mode programs.
Segment registers in the x86-64 architecture represent a legacy but still important mechanism for memory segmentation, privilege control, and specialized addressing. Although most segmentation features are deprecated or disabled in 64-bit mode, FS and GS remain critical for accessing thread-local and system-specific data.
A well-designed assembler must provide full support for segment register usage across all processor modes, including correct encoding of segment overrides, segment loads, and base manipulation instructions. This ensures compatibility with legacy code, system-level programming, and modern software designs leveraging thread-local storage and secure execution contexts.