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?
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.)