无法再使用静态TLS加载任何对象

Cannot load any more object with static TLS

本文关键字:TLS 加载 任何 对象 静态      更新时间:2023-10-16

我有一个使用dlopen()加载附加模块的应用程序。该应用程序和模块是在Ubuntu 12.04 x86_64上使用gcc 4.6构建的,但适用于i386 arch。然后,二进制文件被复制到另一台操作系统完全相同的机器上,并正常工作。

然而,如果它们被复制到Ubuntu 12.04 i386,那么一些(但不是所有)模块将无法加载,并显示以下消息:

dlopen: cannot load any more object with static TLS

我怀疑这是由__thread变量的使用引起的。然而,这样的变量不会在加载的模块中使用,只会在加载程序模块本身中使用。

有人能提供任何额外的信息吗?原因是什么?

我正在减少__thread变量的数量并优化它们(使用-ftls-model等),我只是好奇为什么它在几乎相同的系统上不起作用。

我怀疑这是由__thread变量的使用引起的。

正确。

然而,这样的变量不会在加载的模块中使用,只会在加载程序模块本身中使用。

不正确。您自己可能没有使用__thread,但您静态链接到模块中的某些库正在使用它们。您可以通过以下方式确认:

readelf -l /path/to/foo.so | grep TLS

原因是什么?

模块正在使用-ftls-model=initial-exec,但应该使用-ftls-model=global-dynamic。当链接到foo.so中的(一些)代码在没有-fPIC的情况下构建时,最常发生这种情况。

将非-fPIC代码链接到共享库在x86_64上是不可能的,但在ix86上是允许的(这会导致许多微妙的问题,比如这个问题)。

更新:

我有一个没有-fPIC编译的模块,但我根本没有设置tls模型,据我所知,默认值不是初始exec

  • 每个ELF映像(可执行或共享库)只能有一个tls模型
  • 对于非-fPIC代码,TLS模型默认为initial-exec

因此,如果您甚至将一个使用__thread的非-fPIC对象链接到foo.so,则foo.so将为其TLS的所有获取initial-exec

那么,为什么它会引起问题呢?因为如果使用初始exec,那么tls变量的数量就会受到限制(因为它们不是动态分配的)?

正确。