__readgsqword only available in kernel mode?

Just finished watching episode 182.

I was surprised when the code worked after Casey added __readgsqword() since both the Stack Overflow answer that Google found and the MSDN docs say that this intrinsic is "only available in kernel mode"

Why did it work? Is it simply that MSDN is wrong?

Edited by Sergio González on Reason: s/compiled/worked
I was pretty surprised by the "only in kernel mode" part myself, because it seemed very clear from our ASM exploration that GetCurrentThreadId() or whatever it was did not transition to Kernel mode to access that segment. So it seems like incorrect documentation to me...

- Casey
Probably some artifact left from older Windows or Visual Studio versions.

As Casey told on stream MSVC TLS uses gs register. So it is perfectly valid to access it. Here's example:
1
2
3
4
5
6
__declspec(thread) int ThreadLocalVariable;

int main()
{
  ThreadLocalVariable = 1;
}


Compile it with:
1
cl /O2 /Fa /c tls.c


And look at assembly file tls.asm:
1
2
3
4
5
	mov	ecx, DWORD PTR _tls_index
	mov	rax, QWORD PTR gs:88
	mov	edx, OFFSET FLAT:ThreadLocalVariable
	mov	rax, QWORD PTR [rax+rcx*8]
	mov	DWORD PTR [rdx+rax], 1

It accesses memory location using gs register.
That's for 64-bit code.

For 32-bit code it will use fs register:
1
2
3
4
	mov	ecx, DWORD PTR __tls_index
	mov	eax, DWORD PTR fs:__tls_array
	mov	eax, DWORD PTR [eax+ecx*4]
	mov	DWORD PTR _ThreadLocalVariable[eax], 1


You might want to figure out GetCurrentThreadId works for 32-bit code and wrap our GetThreadID function in #ifdef macro depending on 32-bit/64-bit mode.