Is there an agreed upon convention for operating systems specifying what each table index should describe? For example, on Windows systems (as described here), entry 4 describes 32 bit usermode code (RPL = 3
), and entry 6 describes 64 bit usermode code. Is this a convention? What about other entries?

- 33,889
- 7
- 43
- 76

- 1,536
- 2
- 15
- 23
-
Note the edit to my answer: Brendan pointed out some things I missed. – Peter Cordes Jul 27 '21 at 10:53
1 Answers
x86-64 syscall
/sysret
, and 32-bit sysenter
/sysexit
, apparently do care about the order of your GDT entries: kernel CS, kernel data, user CS, user data in that order. (Thanks @Brendan for that detail.) Or at least, syscall
loads fixed values into the CS and SS internal state:
SYSCALL loads the CS and SS selectors with values derived from bits 47:32 of the IA32_STAR MSR. However, the CS and SS descriptor caches are not loaded from the descriptors (in GDT or LDT) referenced by those selectors. Instead, the descriptor caches are loaded with fixed values. See the Operation section for details. It is the responsibility of OS software to ensure that the descriptors (in GDT or LDT) referenced by those selector values correspond to the fixed values loaded into the descriptor caches; the SYSCALL instruction does not ensure this correspondence.
Other than that, the hardware doesn't care; any pattern that makes sense to you is fine if you only use legacy system-call mechanisms like int 0x80
.
AFAIK, there's no standard convention across OSes, but it's not something I've looked at.
The entries are probably fetched through cache when the kernel sets them (e.g. on context switches), so there could be a tiny advantage to placing ones used together into the same 64-byte cache line. (Reducing cache misses / number of cache lines of footprint.) Especially for 32-bit user-space if you can't just use the null selector (0
) for their SS/DS/ES.
But if the whole GDT is 8 entries or less, the whole thing fits in one cache line (if you align the start by 64). Older CPUs (before Pentium 4 / Core 2) had 32-byte cache lines, but those didn't support 64-bit mode so fewer GDT entries were needed.
Note that the index 0 will never actually be accessed by the CPU, so you can align the first "real" GDT entry and lgdt
with that address minus 8. (The "null descriptor" is a special case.)

- 328,167
- 45
- 605
- 847
-
2Note that (for protected mode and long mode) both `sysenter`/`sysexit` and `syscall`/`sysret` expect the GDT entries they use to be in a specific ("kernel CS, kernel data, user CS, user data") order. Also the null descriptor isn't ever accessed by the CPU - you can have "8 entries excluding null" (9 entries total) in a cache line if you make sure "&gdt[1]" is aligned to the start of the cache line (and the unused "&gdt[0]" is not aligned). – Brendan Jul 27 '21 at 06:18
-
@Brendan: Thanks, updated. If there's anything more to it than what my answer currently says, maybe you should write an answer with details on how / why those instructions make it necessary (or at least a good idea) to lay out the GDT a certain way. – Peter Cordes Jul 27 '21 at 10:52
-
@Brendan could you please point me at the section in AMD or Intel manuals that describes this order? – Omar Darwish Jul 27 '21 at 16:34
-
1@OmarDarwish: For (last year's version of) Intel's manual it'd be "Section 5.8.7 Performing Fast Calls to System Procedures with the SYSENTER and SYSEXIT Instructions" and "Section 5.8.8 Fast System Calls in 64-Bit Mode". For (a much older version of) AMD's manual it's "Section 6.1 Fast System Call and Return". For whichever versions of the manuals you have the relevant parts could be elsewhere (renumbered due to other changes) - best to just search the system programmer's guides (for both manufacturers) for the word `sycall`. – Brendan Jul 27 '21 at 18:19
-
1It's not entirely correct to say that ```sysret``` and ```sysexit``` require the GDT arrangement to be the same. When returning to 64-bit user code, ```sysret``` sets ```CS``` to ```IA32_STAR[48:63] + 16``` and ```SS``` to ```+ 8```, meaning user data has to come first. I think they did that so you could use the same GDT (kernel CS, kernel data, user 32-bit CS, user data, user 64-bit CS) for both protected mode and long mode. – sj95126 Jul 27 '21 at 22:38