I am trying to reverse an ELF binary that makes use of thread-local variables. I can't figure out how to make Ghidra display them in a friendlier way (something like declaring a struct or similar).

For example, this is how a call to scanf with a FILE* stored in TLS looks like:

scanf(*(FILE **)(in_FS_OFFSET + 0xfffffff0),"%d",&num);

Ideally, I would like this to look something like:

scanf(tls->outfile, "%d", &num);

Is it possible to make it more user-friendly? I looked around but I haven't seen anyone complaining about this.

Here's an example code:

#include <threads.h>
#include <stdio.h>
#include <assert.h>

thread_local int foo = 0;

int main(void) {
    assert(scanf("%d", &foo) == 1);
    printf("foo is: %d\n", foo);
    return 0;

tls.c compile with CFLAGS="-std=c11 -Wall -Wextra -O0 -ggdb -Werror" make tls

And here's the decompiled code:

Ghidra's decompiler output for tls binary

  • Okay, so it does indeed handle it differently at -O0 (and even at -O3 for that matter) to static or a mere global variable. However, what's shown as in_FS_OFFSET presumably is fs:0 and this answers how and why -4 is needed here. Given your answer, I think however, you're conflating things a bit.
    – 0xC0000022L
    Commented Jul 25, 2022 at 7:35
  • 1
    In my answer, they propose a way to support having pointers to the middle of a struct, enabling having negative offsets. If that were supported, you could retype in_FS_OFFSET as a pointer to the end of your struct, and then it would automatically recognize your thread_local fields.
    – Marco
    Commented Jul 27, 2022 at 2:38
  • yes you could do that. However, just like when you see access to the TEB or PEB via one of the selectors on Windows, this is a particular "well-known construct" particular to Linux that should be handled (and is, evidently handled, given the name it is assigned) accordingly. That doesn't make this a struct, however. I really don't see it as of yet.
    – 0xC0000022L
    Commented Jul 27, 2022 at 6:49

1 Answer 1


It seems like this feature is not yet implemented in Ghidra.

  • What does this have to do with thread-local variables? All I can see in the defect report is that there is no way to properly access structs from a linked list, where the linked list members have an LIST_ENTRY as their member. This is a common scheme in NT and support for CONTAINING_RECORD therefore a sensible request. But where is the connection to thread-local variables?
    – 0xC0000022L
    Commented Jul 25, 2022 at 7:16
  • The question is about supporting pointers to the middle of a struct, it has nothing to do with lists. In this case, if you define a struct that would be "thread_local_vars", and get your pointer to the end of it, you should be able to get Ghidra to recognize it, right?
    – Marco
    Commented Jul 27, 2022 at 2:32

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.