1

I am trying to use thread local storage in Rust on bare bones. I initially set the FsBase MSR to the target value. This is also confirmed by reading the FsBase register again and by running info registers fs_base in gdb.

When I try to read a thread local variable, rust generates the following asm code (disassembly from gdb)

mov    %fs:0x0,%rax
mov    -0x2000(%rax),%cl
mov    %cl,-0x189(%rbp)

When i try to step through these instructions, I observe that after instruction 1, the value of %rax stays as 0x0 rather than using the value in FsBase. This is not expected behavior.

In my case, the FsBase is set to 0xffffc00000002000. After running into instruction 3, the cpu raises a page fault exception with the target location being 0xffffffffffffe000 meaning that the instruction is trying to load data from 0x0 - 0x2000 rather than an offset from 0xffffc00000002000. This is not the expected behavior.

Are there more steps required to instantiate fs registers?

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
Vinay Chandra
  • 502
  • 5
  • 18
  • 1
    `mov %fs:0x0,%rax` is a load (from thread-local storage, which later gets used as a pointer). If RAX = 0 afterwards, the qword in memory at `FsBase` was zero. Did you expect something else? – Peter Cordes Jun 07 '20 at 02:07
  • Yes, my `FsBase` is no non-zero high memory value. (`0xffffc00000002000`) – Vinay Chandra Jun 07 '20 at 02:08
  • 1
    But `mov` is a load from memory at that address, not `rdfsbase %rax`. There's one extra level of indirection you don't seem to be accounting for. – Peter Cordes Jun 07 '20 at 02:09
  • This is the compiler generated code for accessing thread local storage. I made sure i did not skip any more instructions to load the data. – Vinay Chandra Jun 07 '20 at 02:14
  • 2
    The compiler is assuming that operating system and/or runtime environment has setup thread local storage in a particular way. It's assuming that a pointer to the thread local storage is located at `%fs:0`, but you have the value 0 there instead. The compiler may also be assuming other things not shown by fragment of generated code you've shown. It's not sufficient for you to simply set FS.Base to some value. – Ross Ridge Jun 07 '20 at 03:30
  • 2
    Based on the code sequence generated it's likely your compiler is using the TLS extensions to the System V ABI defined in the document titled "ELF Handling for Thread-Local Storage". – Ross Ridge Jun 07 '20 at 04:00

1 Answers1

3

The code generation here is according to System V ABI as Ross mentioned in the comment. The ABI requires %fs:0 to be set to the value of %fs itself.

So, here, setting the value at address 0xffffc00000002000 to be 0xffffc00000002000 satisfies the ABI and uses the FsBase as expected.

In other words, mov %fs:0x0, %rax is trying to access the value at FsBase and not at 0x0. The value at FsBase just happens to be 0.

Vinay Chandra
  • 502
  • 5
  • 18
  • 2
    Note that this makes sense for performance reasons. For most CPUs, an "`fs` segment override prefix" has a performance cost; so if you want to access thread local variables many times (e.g. in a loop) then it can be faster to convert to a normal address that doesn't use `fs` and use that instead (e.g. load `%fs:0` into a normal register and use `reg+offset` instead of `%fs:offset`). – Brendan Jun 07 '20 at 04:30