TLS是什么

TLS英文全称Thread local Storage,准确的定义是 a computer programming method that uses static or global memory local to a thread。在线程中使用的全局或者static变量,在线程中本地复制一份。最常见的使用场景是errno。每个线程有一份本地的errno,线程间彼此不影响。

在bionic中errno的定义:

1
2
 int* __errno(void) __attribute_const__;
 #define errno (*__errno())

attribute_const const 告诉编译器某个函数是无状态的(也就是说,它使用传递给它的参数生成要返回的结果)。The const function attribute specifies that a function examines only its arguments, and has no effect except for the return value. That is, the function does not read or modify any global memory.// attribute((const)) functions do not read or modify any global memory。 // attribute((const)) functions do not read or modify any global memory 还有就是使用

1
2
__thread int number;
thread_local int tls_var;

存贮位置

在elf中,tls变量存储在elf的tbss和tdata段

img

像其他变量一样,TLS也可以动态链接,如果这样的话,TLS变量将会出现在符号表,相关的type类型为STT_TLS,存储位置:

img

在ARM运行时相关的tls的加载地址为:

img

DTV为dynmic thread vector,向量保存着tls的存储位置。

bionic中TLS的处理

目前bionic不支持ELF TLS,因此在编译时使用了-emulated-tls,相关的soong代码如下:

1
2
3
4
            // Work around bug in Clang that doesn't pass correct emulated
            // TLS option to target. See b/72706604 or
            // https://github.com/android-ndk/ndk/issues/498.
            flags.LdFlags = append(flags.LdFlags, "-Wl,-plugin-opt,-emulated-tls")

libc中相关的处理,对于tls的

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
        if (ELF_ST_TYPE(s->st_info) == STT_TLS) {
          DL_ERR("unsupported ELF TLS symbol \"%s\" referenced by \"%s\"",
                 sym_name, get_realpath());
          return false;
        }
        if (!relocating_linker) {
          if (d->d_tag == DT_TLSDESC_GOT || d->d_tag == DT_TLSDESC_PLT) {
           DL_ERR("unsupported ELF TLS DT entry in \"%s\"", get_realpath());
            return false;
         }

对于errno处理比较特殊,专门放到了tcb中,相关的代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// Well-known TLS slots. What data goes in which slot is arbitrary unless otherwise noted.
enum {
  TLS_SLOT_SELF = 0, // The kernel requires this specific slot for x86.
  TLS_SLOT_THREAD_ID,
  TLS_SLOT_ERRNO,

  // These two aren't used by bionic itself, but allow the graphics code to
  // access TLS directly rather than using the pthread API.
  TLS_SLOT_OPENGL_API = 3,
  TLS_SLOT_OPENGL = 4,

  // This slot is only used to pass information from the dynamic linker to
  // libc.so when the C library is loaded in to memory. The C runtime init
  // function will then clear it. Since its use is extremely temporary,
  // we reuse an existing location that isn't needed during libc startup.
  TLS_SLOT_BIONIC_PREINIT = TLS_SLOT_OPENGL_API,

  TLS_SLOT_STACK_GUARD = 5, // GCC requires this specific slot for x86.
  TLS_SLOT_DLERROR,

  // Fast storage for Thread::Current() in ART.
  TLS_SLOT_ART_THREAD_SELF,

  // Lets TSAN avoid using pthread_getspecific for finding the current thread
  // state.
  TLS_SLOT_TSAN,

  BIONIC_TLS_SLOTS // Must come last!
};

int*  __errno() {
  return reinterpret_cast<int*>(&(__get_tls()[TLS_SLOT_ERRNO]));
}

但是从android 10.0的最新代码看,ELF TLS支持已经添加进去。