Handmade Hero » Forums » Code » __readgsqword only available in kernel mode?
serge_rgb
Sergio González
31 posts
1 project

I'm working on Milton, a paint program with infinite canvas. My mom thinks I'm awesome.

#4706 __readgsqword only available in kernel mode?
2 years, 1 month ago Edited by Sergio González on Sept. 10, 2015, 9:06 p.m. Reason: s/compiled/worked

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?
cmuratori
Casey Muratori
803 posts
1 project

Casey Muratori is a programmer at Molly Rocket on the game 1935 and is the host of the educational programming series Handmade Hero.

#4711 __readgsqword only available in kernel mode?
2 years, 1 month ago

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
mmozeiko
Mārtiņš Možeiko
1485 posts
1 project
#4713 __readgsqword only available in kernel mode?
2 years, 1 month ago

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.