Global Descriptor Table
The Global Descriptor Table (GDT) is a data structure in the Intel x86 architecture used in protected mode to define and manage memory segments through a table of 8-byte segment descriptors that specify base addresses, limits, access rights, and other attributes for code, data, and system segments.[1] It serves as a core component for enabling memory segmentation, which translates logical addresses to linear addresses while enforcing protection mechanisms such as limit checking, type validation, and privilege-level restrictions across rings 0 through 3.[1] The GDT is stored in system memory as a linear array of descriptors, with the first entry reserved as a null descriptor to prevent unintended access, and it can hold up to 8192 entries, though the actual size is defined by a 16-bit limit in the GDTR register (Global Descriptor Table Register).[1] Each descriptor includes fields for the 32-bit segment base address, a 20-bit segment limit (scalable via a granularity flag for 4KB units), access rights (encompassing present bit, descriptor privilege level or DPL, and type indicators for code, data, or system usage), and mode-specific flags like the default operation size (D/B) or long mode (L) bit for IA-32e compatibility.[1] The GDTR itself holds the GDT's base address (32 bits in protected mode, expanded to 64 bits in IA-32e mode) and is loaded using the privileged LGDT instruction, typically by the operating system kernel in ring 0, with the table aligned on an 8-byte boundary for optimal performance.[1] In protected mode operations, the GDT supports essential system functions including multitasking via Task State Segment (TSS) descriptors, inter-privilege-level transfers through call gates, and integration with the Interrupt Descriptor Table (IDT) for handling exceptions and interrupts. The GDT contains segment descriptors, including system descriptors for LDTs and TSS, while the IDT is a separate table for interrupt and exception handling.[1] Although segmentation via the GDT enables both flat memory models (with a single large segment) and multi-segment configurations for fine-grained control, its role diminishes in 64-bit IA-32e mode where most segmentation is disabled except for the FS and GS segments used for thread-local storage.[1] Upon processor reset, the GDTR initializes to a base of 0 and limit of 0xFFFF, necessitating explicit OS initialization to enable protected mode functionality.[1]Background and Purpose
Definition and Role in x86 Architecture
The Global Descriptor Table (GDT) is a fundamental data structure in the x86 architecture that contains an array of 8-byte segment descriptor entries. Each entry defines the attributes of a memory segment, including its base address, size limit, access rights, privilege level, and granularity, enabling the processor to manage distinct regions of memory for code, data, stacks, and system resources.[1] The GDT is located anywhere in the linear address space and is referenced by the processor through the Global Descriptor Table Register (GDTR), which holds the table's base address and limit.[1] In protected mode operation, the primary role of the GDT is to facilitate memory segmentation, where logical addresses—composed of a segment selector and an offset—are translated into linear addresses by the CPU. Segment selectors, loaded into the six segment registers (CS for code, DS/ES/FS/GS for data, and SS for stack), index into the GDT to retrieve the corresponding descriptor and apply its attributes during address resolution. This mechanism enforces memory protection by checking access permissions, segment limits, and privilege levels (such as ring 0 for kernel mode and ring 3 for user mode), preventing unauthorized access and ensuring isolation between different execution environments.[1] The GDT thus supports essential features like multitasking, where multiple processes can share memory safely, and privilege separation to mitigate security risks from faulty or malicious code.[1] Key benefits of the GDT include enabling code and data separation, which allows executable segments to be distinguished from writable data segments, and facilitating dynamic memory management in operating systems. For instance, in a typical OS kernel, the GDT might define a code segment with ring 0 privileges for kernel execution and a separate data segment with ring 3 privileges for user-space applications, ensuring that user code cannot directly access kernel memory.[1] This structure underpins the x86's segmented memory model, providing a foundational layer for virtual memory and interrupt handling without relying on flat addressing alone.[1]Memory Segmentation Fundamentals
Memory segmentation in the x86 architecture divides the virtual memory space into variable-sized, independent segments to enable memory protection, isolation of program components, and efficient addressing mechanisms, in contrast to flat memory models that treat the address space as a single continuous region.[2] This approach allows programs to operate within designated memory regions, such as code, data, or stack segments, preventing unauthorized access and supporting multitasking by logically separating address spaces.[2] Segmentation was introduced with the Intel 80286 processor to overcome the limitations of real-mode addressing in earlier processors like the 8086, which restricted memory access to a 1 MB flat space without protection, thereby enabling protected mode operations for reliable multitasking and enhanced security.[2] In x86 addressing, memory references use logical addresses composed of a segment selector and an offset, which the processor translates into a linear address by adding the offset to the segment's base address; this linear address is then mapped to a physical address via paging if enabled.[2] Specifically, the logical address translation follows the formula where the linear address equals the segment base address plus the offset, ensuring that all memory accesses are resolved relative to segment boundaries before further translation to physical memory.[2] This two-stage process—segmentation followed by paging—provides layered abstraction, allowing flexible virtual-to-physical mapping while enforcing segment-level protections.[2] Each segment is defined by key attributes that control its location, size, and access permissions, including a 32-bit base address specifying the segment's starting location in the linear address space, and a 20-bit limit field that defines the segment's size, which can be scaled by a granularity bit to units of 1 byte or 4 KB for larger extents up to 4 GB.[2] Access rights attributes govern operations such as read, write, and execute permissions, while the descriptor privilege level (DPL), ranging from 0 (most privileged) to 3 (least privileged), enforces ring-based protection to restrict access based on the current privilege level.[2] Additional flags, such as the conforming bit for code segments (allowing access from less privileged levels under certain conditions) and the expand-down bit for data segments (enabling growth downward from the limit), provide flexibility for specific use cases like stacks.[2] These segment definitions are stored in descriptor tables, such as the Global Descriptor Table.[2]Structure and Format
Organization of the GDT
The Global Descriptor Table (GDT) is structured as a variable-length array of 8-byte segment descriptors stored in system memory, forming the basis for memory segmentation in protected mode. The table always begins with a null descriptor at index 0, which is defined as all zeros and deemed invalid for any segment selector, functioning as a safeguard against the use of uninitialized segment registers by causing a general-protection exception if referenced. Following this, the table accommodates up to 8191 additional descriptors starting at index 1, with the total maximum of 8192 entries limited by the 64 KB capacity derived from the GDTR's 16-bit limit field.[1] The GDT resides at a location in linear address space specified by the Global Descriptor Table Register (GDTR), a 48-bit register in IA-32 architecture consisting of a 32-bit base address pointing to the table's starting byte and a 16-bit limit field indicating the table's size in bytes minus one, with a maximum value of 0xFFFF for 65536 bytes. For efficiency, the base address is recommended to be aligned on an 8-byte boundary. The limit enforces sizing constraints by requiring the table length (limit + 1) to be a multiple of 8 bytes to encompass complete descriptor entries without partial overlaps.[1] Entries within the GDT are sequentially numbered from 0, enabling straightforward indexing where the processor computes an entry's address by multiplying the selector's 13-bit index by 8 and adding it to the GDTR base. Operating systems commonly reserve the initial low indices for essential fixed-purpose segments, such as index 1 for a flat code segment and index 2 for a flat data segment in minimalist configurations, thereby standardizing access patterns across implementations. This array organization relies on the uniform 8-byte descriptor entries as its core building blocks for defining segment attributes.[1]Descriptor Entry Components
Each entry in the Global Descriptor Table (GDT) is an 8-byte structure that defines the attributes of a memory segment or system resource in the IA-32 architecture's protected mode.[1] This format allows the processor to enforce memory protection, privilege levels, and access controls by specifying the segment's base address, size (limit), and operational characteristics.[1] The layout divides the 64 bits into fields for the base address (32 bits total), segment limit (20 bits total), access rights, and flags, enabling flexible segment definitions up to 4 GB in size when using granularity.[1] The byte-level organization of a GDT entry is as follows:| Byte Offset | Bits | Field Description |
|---|---|---|
| 0-1 | 0-15 | Segment Limit (lower 16 bits) |
| 2-3 | 0-15 (of base) | Base Address (lower 16 bits) |
| 4 | 16-23 (of base) | Base Address (bits 16-23) |
| 5 | Access Rights | Presence (P, bit 7), Descriptor Privilege Level (DPL, bits 5-6), System/User Segment (S, bit 4), Type (bits 0-3) |
| 6 | Flags and Upper Limit | Base Address (bits 24-31 in byte 7, but flags here: Granularity (G, bit 7), Default/Big (D/B, bit 6), Reserved (bit 5), Available (AVL, bit 4), Limit (bits 16-19 in bits 0-3) |
| 7 | 24-31 (of base) | Base Address (upper 8 bits) |
Loading and Access Mechanisms
GDTR Register and Initialization
The Global Descriptor Table Register (GDTR) is a processor register that holds the base address and limit of the Global Descriptor Table (GDT) in memory, enabling the x86 processor to locate and access segment descriptors during protected mode operation. In IA-32 protected mode, the GDTR is a 48-bit register comprising a 32-bit base address field, which specifies the linear starting address of the GDT, and a 16-bit limit field, which defines the size of the GDT in bytes (ranging from 0 to 64 KB - 1). In x86-64 long mode (IA-32e mode), the GDTR expands to an 80-bit structure with a 64-bit base address, where the upper 32 bits must be sign-extended for canonical addressing to ensure compatibility with the 48-bit virtual address space, while the limit remains 16 bits with its upper bits zeroed. The base address must be aligned, typically on an 8-byte boundary for optimal performance, and the GDT must reside in accessible, readable memory, such as writeback cacheable regions.[3] The GDTR is loaded using the LGDT (Load Global Descriptor Table Register) instruction, which is a privileged operation requiring execution at current privilege level (CPL) 0 and is unavailable in virtual-8086 mode. LGDT reads a 6-byte operand from memory in protected mode—consisting of the 16-bit limit followed by the 32-bit base—or a 10-byte operand in IA-32e mode, directly updating the GDTR fields without affecting other registers. This instruction is serializing, guaranteeing that all prior instructions complete before the load, and it can be executed in both real and protected modes, though its utility is primarily in protected mode initialization. After loading the GDTR, operating systems typically load segment registers (such as CS, DS, ES, FS, GS, and SS) using instructions like LSS (Load Stack Segment) or far jumps to establish initial segment contexts based on GDT entries.[3] Initialization of the GDTR occurs during operating system boot or task context switches to ensure the GDT is properly referenced for segmentation. The process begins with the OS allocating and populating the GDT in physical memory with at least a null descriptor at the first entry, followed by issuing LGDT to set the base and limit in the GDTR. This setup is essential before enabling protected mode, as the processor references the GDTR for all segment loads and descriptor validations thereafter. In multi-tasking environments, the GDTR contents may be reloaded during context switches if the GDT needs relocation, though modern systems often maintain a fixed GDT to minimize overhead. The initialization must occur in a memory region that is page-aligned and fault-free to avoid disruptions.[3] Invalid GDTR configurations trigger exceptions to enforce memory safety. If the LGDT operand is misaligned, exceeds the 6- or 10-byte size, references non-canonical addresses in long mode, or points to inaccessible memory, a general protection fault (#GP) is raised; similarly, a limit value greater than 0xFFFF or execution at CPL > 0 causes #GP. Page faults (#PF) may occur if the GDT memory is not present or protected, and in virtual machine extensions (VMX), invalid GDTR states during VM exits can lead to VMX aborts. These faults ensure the GDT remains a reliable segmentation foundation, with the processor halting execution until corrected by the OS handler.[3] In real mode, the GDT and GDTR are not utilized for segmentation, as the processor operates with a flat 1 MB address space; following reset, the GDTR defaults to a base of 0x00000000 and limit of 0xFFFF, but these values are ignored. Transition to protected mode requires first initializing the GDT and loading the GDTR via LGDT in real mode, then setting the protection enable (PE) bit in CR0 using the LMSW (Load Machine Status Word) instruction, followed by a far jump to reload the code segment (CS) from a GDT selector. This sequence activates segmentation, making the GDTR active and enabling descriptor-based memory protection upon entry to protected mode.[3]Segment Selectors and Resolution
A segment selector is a 16-bit value that serves as an identifier for a segment descriptor within the Global Descriptor Table (GDT) or Local Descriptor Table (LDT). It consists of three fields: a 13-bit index (bits 3 through 15), which specifies the entry number in the descriptor table starting from the first entry after the null descriptor; a table indicator (TI) bit (bit 2), where 0 selects the GDT and 1 selects the LDT; and a 2-bit requested privilege level (RPL, bits 0 and 1), which indicates the privilege level of the selector for access control purposes.[4] The index field determines the position of the descriptor in the table, while the RPL provides an additional check against the current privilege level (CPL) derived from the code segment selector. The resolution process begins when the CPU loads a segment selector into a segment register, such as CS, DS, SS, ES, FS, or GS. The processor uses the TI bit to determine whether to access the GDT or LDT; for the GDT, it takes the base address from the GDTR register and computes the byte offset by multiplying the index by 8 (shifting left by 3 bits, as each descriptor is 8 bytes long) and adding it to the base. This offset locates the corresponding descriptor entry, which is then fetched and validated. The present (P) bit in the descriptor must be set to 1; otherwise, a #NP fault occurs. Additional checks include verifying the descriptor type (e.g., code or data), segment limit, and granularity to ensure the offset and access are within bounds; if invalid, a #GP fault is raised. For conforming code segments, the descriptor's accessed bit may also be set during resolution.[4] Privilege checks are integral to the resolution to enforce protection rings. The CPL, obtained from bits 0-1 of the CS selector, is compared against the descriptor's descriptor privilege level (DPL) and the selector's RPL. For data segments, access is permitted only if DPL ≥ CPL and DPL ≥ RPL; violation results in a #GP fault. For nonconforming code segments, the CPL must equal the DPL. In contrast, conforming code segments allow access if CPL ≥ DPL, enabling less privileged code (higher CPL) to execute more privileged code (lower DPL). Stack segment (SS) selectors require the DPL to exactly match the CPL, and inter-segment control transfers must satisfy both RPL and CPL relative to the target DPL to prevent privilege escalation. These checks occur at load time and during address translation.[4] Once resolved and validated, the descriptor's contents— including base address, segment limit, and access rights—are cached in the hidden portion of the segment register for efficient subsequent access. This caching avoids repeated lookups in the GDT during memory operations within the segment, improving performance. The Load Segment Limit (LSL) instruction can verify the cached limit of a selector by loading it into a general-purpose register if valid, or it signals invalidity via the zero flag. Similarly, the Load Access Rights Byte (LAR) instruction checks and loads the access rights byte from the cached descriptor. In cases of segment register loads via instructions like MOV or jumps, the cache is updated only after successful resolution and checks.[4]| Field | Bits | Description |
|---|---|---|
| Index | 3-15 | Entry number in GDT/LDT (13 bits, multiplied by 8 for offset) |
| TI | 2 | 0 = GDT, 1 = LDT |
| RPL | 0-1 | Requested privilege level (0 most privileged, 3 least) |
Usage Across Processor Modes
In IA-32 Protected Mode
In IA-32 protected mode, the Global Descriptor Table (GDT) becomes fully operational following the transition from real-address mode, a feature introduced with the 80286 processor and required for all subsequent x86 processors operating in this environment. Protected mode is activated by setting the protection enable (PE) bit in the CR0 control register, which mandates the use of the GDT for memory segmentation to enforce hardware-based protection mechanisms such as privilege levels and access controls, while paging remains an optional extension for virtual memory management.[1] Once enabled, the processor relies on segment descriptors within the GDT to define the boundaries and attributes of memory segments, ensuring isolation between user and kernel spaces or different protection rings. Common configurations of the GDT in protected mode often employ a flat memory model, where a single code segment and a single data segment each span the entire 4 GB linear address space with a base address of 0 and a limit of 4 GB (granularity bit set), simplifying address translations and reducing overhead for modern operating systems. Alternatively, a multi-segment model is used for legacy applications or environments requiring granular protection, defining separate segments for code, data, and stack with distinct base addresses, limits, and access rights to prevent unauthorized overlaps or executions.[1] These setups leverage descriptor types (e.g., executable code or writable data) and segment selectors to enforce access rules based on the current privilege level. Inter-segment operations in protected mode involve loading new segment selectors into registers like CS (code segment) or DS (data segment) during far jumps or calls, which trigger descriptor resolution from the GDT and privilege checks to ensure the target segment's descriptor privilege level (DPL) matches or is more privileged than the selector's requested privilege level (RPL) and the current privilege level (CPL). For privilege level changes, such as entering a more privileged ring, the stack segment register (SS) must be updated to point to a conforming stack segment in the GDT, often facilitated through call gates or task state segments to maintain stack integrity across rings.[1] Fault handling in protected mode detects violations related to GDT-defined segments, generating a stack-segment fault (#SS) when operations exceed the stack segment's limit or reference a non-present stack segment (present bit P=0 in the descriptor). Similarly, a segment-not-present fault (#NP) occurs upon attempting to load a selector for a segment where the present bit is clear, preventing access to unloaded or invalid descriptors and allowing the operating system to handle paging or loading as needed.[1]In x86-64 Long Mode
x86-64 long mode, also known as IA-32e mode, was introduced by AMD in 2003 with the Opteron processor family, marking the first implementation of a 64-bit extension to the x86 architecture.[5] This mode encompasses two sub-modes: a 64-bit mode for native 64-bit operation and a compatibility sub-mode that supports 32-bit x86 applications, enabling backward compatibility while expanding addressable memory to 64 bits. In long mode, the Global Descriptor Table (GDT) persists from earlier x86 modes but undergoes substantial simplification, as segmentation is largely supplanted by paging for memory protection and addressing. The GDT's role shifts from defining complex segment hierarchies to primarily supporting system-level constructs and compatibility features.[1][6] In long mode, the GDT adopts a flat memory model where code and data segments have their base addresses fixed at 0 and limits set to the maximum value (typically 0xFFFFF for 20-bit granularity or expanded equivalents), rendering traditional segmentation attributes largely irrelevant for most operations. The code segment (CS) base is always 0, and its limit is effectively unlimited, with no enforcement of segment boundaries for data segments (DS, ES, SS), which are treated as having base 0 and ignoring limits and attributes. This flattening eliminates the need for multiple data segment descriptors, as paging handles virtual-to-physical address translation and protection. However, the FS and GS segments retain utility for thread-local storage, with their base addresses configurable not only via GDT descriptors but also through Model-Specific Registers (MSRs) like IA32_FS_BASE and IA32_GS_BASE, allowing 64-bit linear addresses without GDT dependency.[1][6] A minimal GDT suffices for long mode operation, typically comprising a null descriptor as the first entry (selector 0, unused), a 64-bit code segment descriptor (with the L-bit set for long mode and D-bit clear), and optionally a 32-bit code descriptor for compatibility sub-mode support. System descriptors, such as the 64-bit Task State Segment (TSS), remain required for interrupt stack tables and task switching remnants, though hardware task management is disabled. Segment selectors continue to be used in instructions and registers, but their interpretation is simplified: privilege levels (DPL) and type fields are checked, yet expand-down data segments are unsupported, and 16/32-bit gates trigger general protection faults (#GP). Overall, these adaptations reduce the GDT's complexity, prioritizing paging for security while retaining selectors for legacy and system purposes.[1][6] Limitations in long mode further constrain GDT functionality to ensure compatibility and performance: segment limits are ignored for all data segments, including FS/GS; no upper limit checking occurs beyond canonical address validation in 64-bit mode. Task gates and call gates are restricted—only 64-bit call gates are permitted, and attempts to use legacy gates or hardware task switches result in #GP exceptions. The GDTR register expands to a 64-bit base, but the GDT size remains capped at 64 KB (8192 entries), and initial loading must occur within the lower 4 GB address space before relocation. These constraints underscore the de-emphasis on segmentation, making the GDT a vestigial structure in modern 64-bit systems.[1][6]Related Tables and Extensions
Local Descriptor Table
The Local Descriptor Table (LDT) serves as a task-specific counterpart to the Global Descriptor Table (GDT) in the x86 architecture, storing segment descriptors that define memory segments unique to an individual task or process.[7] Managed by the operating system, the LDT enables private segmentation for code, data, and stack areas, facilitating memory isolation and selective sharing among tasks in a multitasking environment.[7] Unlike the system-wide GDT, the LDT is optional and per-task, allowing each process to maintain its own set of segment definitions without interfering with others.[7] The LDT itself is referenced through a dedicated entry in the GDT, which acts as a system descriptor pointing to the LDT's location and size.[7] Structurally, the LDT mirrors the GDT in format, consisting of an array of 8-byte segment descriptor entries in IA-32 protected mode (expanding to 16 bytes in IA-32e mode to accommodate 64-bit base addresses).[7] Its size is variable, defined by a limit field that supports up to 8192 entries (64 KB total) with byte granularity or larger extents when the granularity flag is set.[7] The Local Descriptor Table Register (LDTR) holds the necessary addressing information: a 16-bit segment selector that identifies the LDT descriptor within the GDT, a 32-bit base address pointing to the LDT's linear starting location (64 bits in IA-32e mode), and a 20-bit limit specifying the table's extent.[7] The LDTR is loaded using the privileged LLDT instruction, which requires current privilege level (CPL) 0 and serializes instruction execution to ensure consistency.[7] During task switches in protected mode, the LDTR can be automatically updated from the task's state segment if configured.[7] In operation, the LDT integrates with segment selection via the Table Indicator (TI) bit in segment selectors: when TI=1, the processor routes descriptor lookups to the LDT instead of the GDT (TI=0), enabling seamless use of task-private segments in memory accesses.[7] This mechanism is fully supported in IA-32 protected mode, where it underpins multitasking by providing per-task segmentation without relying on the GDT for all operations.[7] However, the LDT differs from the GDT in scope—it is smaller, process-specific rather than system-wide, and not mandatory for basic operation—reflecting its role in fine-grained task isolation rather than global resource management.[7] In x86-64 long mode, while the LDT remains available for compatibility (with expanded descriptors and limit checking disabled in 64-bit submode), it is largely deprecated in favor of a flat memory model enforced by paging; segment registers like FS and GS instead use model-specific registers (MSRs) to set per-thread bases, bypassing traditional LDT-based segmentation for thread-local storage.[7] Hardware task switching, which could leverage the LDT, is unsupported in long mode, shifting management to software.[7]Task State Segment and System Descriptors
In the x86 architecture, system descriptors within the Global Descriptor Table (GDT) are distinguished by the system bit (S) being set to 0 in the access rights byte, indicating they define system resources rather than user-accessible code or data segments.[1] These descriptors facilitate advanced features such as task management and controlled procedure calls, with common types including Task State Segment (TSS) descriptors, Local Descriptor Table (LDT) pointers, and call gates.[1] TSS descriptors, in particular, support hardware task switching by pointing to a dedicated memory segment that holds the complete state of a task.[1] The TSS descriptor itself is an 8-byte entry in IA-32 protected mode, expanding to 16 bytes in x86-64 long mode to accommodate 64-bit base addresses.[1] It includes fields for the base address of the TSS segment (32 bits in IA-32, 64 bits in x86-64), a segment limit (typically 0x67 for a standard 104-byte 32-bit TSS), type codes (9 for available TSS, 11 for busy TSS), descriptor privilege level (DPL), and present bit (P).[1] The underlying TSS segment, starting from the 80386 processor, is at least 104 bytes long in 32-bit mode and stores critical task context, including general-purpose registers (e.g., EAX, EBX), segment selectors (CS, DS, SS), instruction pointer (EIP), flags (EFLAGS), page directory base (CR3), stack pointers per privilege level (SS0:ESP0 for ring 0, etc.), the LDT selector, and an optional I/O permission bitmap for controlling port access.[1] In x86-64 mode, the TSS extends beyond 104 bytes to include up to seven interrupt stack table (IST) entries for stack switching during interrupts and 64-bit RSP pointers per privilege level.[1] Task switching using a TSS descriptor is initiated in IA-32 protected mode via a far jump (JMP) or call (CALL) to the TSS selector in the GDT, or through a task gate, interrupt, or exception.[1] Upon switching, the processor hardware automatically saves the current task's state into its TSS—including registers, segment selectors, EIP, EFLAGS, and the nested task (NT) flag in EFLAGS—and loads the new task's state from the target TSS, updating the task register (TR) with the new selector.[1] This mechanism enables hardware-supported multitasking, where the busy bit in the TSS descriptor (type 11) prevents re-entrant access to the same task, and the LTR instruction loads the initial TR with a TSS selector during system initialization.[1] The I/O bitmap in the TSS allows fine-grained control over IN/OUT instructions based on the current privilege level.[1] LDT pointers serve as another system descriptor type (type 2), simply referencing the base and limit of a task-specific Local Descriptor Table without additional functionality.[1] Call gates, also system descriptors, enable secure inter-privilege-level transfers for procedure calls with stack switching.[1] In x86-64 long mode, hardware task switching via TSS is deprecated and unsupported, with the TSS repurposed primarily for interrupt stack management through IST entries to prevent kernel stack overflows.[1] Modern operating systems favor software-based task switching—manually saving and restoring context—for greater efficiency and flexibility over the overhead of hardware TSS operations.[1]| TSS Segment Components (IA-32 32-bit Mode) | Description | Offset (Bytes) |
|---|---|---|
| Back Link | Selector of previous task's TSS | 0-1 |
| ESP0, SS0 | Ring 0 stack pointer and selector | 2-7 |
| ESP1, SS1 | Ring 1 stack pointer and selector | 8-13 |
| ESP2, SS2 | Ring 2 stack pointer and selector | 14-19 |
| CR3 (PDBR) | Page directory base register | 20-23 |
| EIP | Instruction pointer | 24-27 |
| EFLAGS | Flags register | 28-31 |
| EAX, ECX, EDX, EBX, ESP, EBP, ESI, EDI | General-purpose registers | 32-63 |
| ES, CS, SS, DS, FS, GS | Segment selectors | 64-75 |
| LDT Selector | Local descriptor table selector | 76-77 |
| I/O Bitmap (Optional) | Permission bitmap for I/O ports | 102+ (variable) |