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.
amd64_set_fsbase((void*)fsbase);
amd64_set_gsbase((void*)gsbase);
Doesn't care.
sysarch(X86_64_SET_FSBASE, (void*)fsbase);
sysarch(X86_64_SET_GSBASE, (void*)gsbase);
The 32 bit ABI for Windows allows you to modify the equivalent of gsbase using an LDT entry.
int (*NtSetLdtEntries)(DWORD, DWORD, DWORD, DWORD, DWORD, DWORD);
*(FARPROC*)(&NtSetLdtEntries) = GetProcAddress(LoadLibrary("ntdll.dll"), "NtSetLdtEntries");
DWORD base = /* ... */;
DWORD limit = /* ... */;
LDT_ENTRY ll;
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
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: http://ghettohaxxx-blog.azurewebsites.net/executing-bsd-elfs-in-windows/
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.
Very nice, thank you so much !