Playing with segment registers fs and gs on x64

Published 3rd April 2017

When you're running out of registers while writing a JIT, you might resort to more unconventional methods for memory access. You might choose to resort to segment registers if you need a fixed register for memory offsets.

Instructions such as:

lea    rax,gs:[rcx+rdx*8]
mov    rax,gs:[rcx+rdx*8]

would then be available for your use.

This document documents what I have found about fs and gs on modern operating systems.


Doesn't care. Do what you want with gs.

#include <asm/prctl.h>
static int arch_prctl(int func, void *ptr) {
    return syscall(__NR_arch_prctl, func, ptr);

arch_prctl(ARCH_SET_FS, (void*)fsbase);
arch_prctl(ARCH_SET_GS, (void*)gsbase);


Doesn't care.



Doesn't care.

sysarch(X86_64_SET_FSBASE, (void*)fsbase);
sysarch(X86_64_SET_GSBASE, (void*)gsbase);

Windows (32-bit)

The 32 bit ABI for Windows allows you to modify the equivalent of gsbase using an LDT entry.

*(FARPROC*)(&NtSetLdtEntries) = GetProcAddress(LoadLibrary("ntdll.dll"), "NtSetLdtEntries");

DWORD base = /* ... */;
DWORD limit = /* ... */;

ll.BaseLow = base & 0xFFFF;
ll.HighWord.Bytes.BaseMid = base >> 16;
ll.HighWord.Bytes.BaseHi = base >> 24;
ll.LimitLow = limit & 0xFFFF;
ll.HighWord.Bits.LimitHi = limit >> 16;
ll.HighWord.Bits.Granularity = 0;
ll.HighWord.Bits.Default_Big = 1;
ll.HighWord.Bits.Reserved_0 = 0;
ll.HighWord.Bits.Sys = 0;
ll.HighWord.Bits.Pres = 1;
ll.HighWord.Bits.Dpl = 3;
ll.HighWord.Bits.Type = 0x13;

int ret = NtSetLdtEntries(0x80, *(DWORD*)&ll, *((DWORD*)(&ll)+1), 0, 0, 0);
assert(ret >= 0);

// Then use assembly to set gs to refer to the LDT entry

Windows (64-bit)

Win 8.1 onwards enables fsgsbase instructions when they are available. This requires the application to be non-UMS.

Win 8.1 onwards explicitly allows you to use the wrgsbase instruction to directly write gsbase for user-level threading. However, the kernel checks for a valid TEB and may modify your gsbase if it doesn't point to a valid one.

Presence of this feature can be detected using IsProcessorFeaturePresent(PF_RDWRFSGSBASE_AVAILABLE).

I currently do not know if wrfsbase is available for use.

Edit: @shuffle2 has a useful post on using fs here:


This looks half-promising.

That syscall is a MDEP syscall intended for the internal use of pthread to set gsbase. Looking at the mdep syscall table shows that it is the only valid mdep syscall on x64, and the rest of the code shows it does what you'd expect.

Scouering the rest of the publicly available kernel sourcecode doesn't reveal any method of setting fsbase.

If one is absolutely desperate, one could use Hypervisor.framework which would provide you with absolute control over a vCPU.

Edit 2: If you really really need to use fs for some reason, see this hack, which rewrites all fs accesses to gs accesses.