Problem

try 运行这个代码

if (!dlopen("../lib/libMy.so", RTLD_NOW)) {
    std::cout << dlerror() << std::endl;
    return 1;
}

导致了TLS(线程本地存储)错误:

无法在静态TLS块中分配内存

Solution

转储线程本地存储部分.tbss(静态和非静态非初始化线程本地变量)显示Tls_ErrorStruct大小为600字节(0x258),Tls_tv大小为1600字节(0x640),总计为2200个TLS字节:

$ objdump -C -t ../lib/libMy.so | grep -F '.tbss'

00000000000008e8 l       .tbss  0000000000000008              runtime.tlsg
0000000000000010 l       .tbss  0000000000000001              __tls_guard
0000000000000688 l       .tbss  0000000000000001              __tls_guard
...
0000000000000690 g       .tbss  0000000000000258              Tls_ErrorStruct
...
0000000000000018 g       .tbss  0000000000000640              Tls_tv

为了修复块错误,这thread_local个变量被减少到约600字节.第dlopen章成功了

Note

判断需要多少线程本地存储(TLS)0x0008f0

$ readelf -Wl ../lib/libMy.so

Elf file type is DYN (Shared object file)
Entry point 0x0
There are 10 program headers, starting at offset 64

Program Headers:
  Type           Offset    VirtAddr           PhysAddr           FileSiz  MemSiz   Flg Align
  LOAD           0x000000  0x0000000000000000 0x0000000000000000 0x48ada8 0x48ada8 R   0x1000
  LOAD           0x48b000  0x000000000048b000 0x000000000048b000 0xdab271 0xdab271 R E 0x1000
  LOAD           0x1237000 0x0000000001237000 0x0000000001237000 0x2c0564 0x2c0564 R   0x1000
  LOAD           0x14f76a0 0x00000000014f86a0 0x00000000014f86a0 0xd16172 0xd6ec50 RW  0x1000
  DYNAMIC        0x20b58c0 0x00000000020b68c0 0x00000000020b68c0 0x000220 0x000220 RW  0x8
  NOTE           0x000270  0x0000000000000270 0x0000000000000270 0x000088 0x000088 R   0x4
  TLS            0x14f76a0 0x00000000014f86a0 0x00000000014f86a0 0x000001 0x0008f0 R   0x8
  GNU_EH_FRAME   0x13fa440 0x00000000013fa440 0x00000000013fa440 0x02358c 0x02358c R   0x4
  GNU_STACK      0x000000  0x0000000000000000 0x0000000000000000 0x000000 0x000000 RW  0x10
  GNU_RELRO      0x14f76a0 0x00000000014f86a0 0x00000000014f86a0 0xbc3960 0xbc3960 R   0x1

Source

Question

我不知道如何找到动态加载器线程本地存储块大小,但确实找到了一个可能的引用here:

/* Size of the static TLS block.  Giving this initialized value
   preallocates some surplus bytes in the static TLS area.  */
size_t _dl_tls_static_size = 2048;

有没有一个linux工具或一些C++方法来确定最大线程本地存储块大小?

推荐答案

这不是一个技术上的答案,因为我不想try 这个,只是一个可能的修复思路.是的,我就是这么懒,当涉及到不影响我的事情时.

对于初学者来说,虽然我们在pthread tls API中有数量有限的"线程本地键",但没有使用该API,所以没关系.相反,Linux使用ELF段作为线程本地存储,这没有任何限制.

您遇到的限制来自dlopen()打开一个初始加载程序没有看到的模块.现在,您发现的_dl_tls_static_size符号似乎在某种程度上可以处理这种情况,只是让加载器分配更多的内存.然而,在这个符号上,你会发现this comment:

  /* That is the size of the TLS memory for this object.  The initialized
     value of _dl_tls_static_size is provided by dl-open.c to request some
     surplus that permits dynamic loading of modules with IE-model TLS.  */

这里突出的词是"IE模型",这意味着可能存在其他不需要这个的"模型".如果你看gcc个选项,你会发现this个:

-ftls-model=model

    Alter the thread-local storage model to be used (see Thread-Local Storage). The model argument should be one of ‘global-dynamic’, ‘local-dynamic’, ‘initial-exec’ or ‘local-exec’. Note that the choice is subject to optimization: the compiler may use a more efficient model for symbols not visible outside of the translation unit, or if -fpic is not given on the command line.

    The default without -fpic is ‘initial-exec’; with -fpic the default is ‘global-dynamic’.

是的,我们有一个模型initial-exec,它可以很容易地缩写为IE,这是我们在 comments 中看到的. 因此,如果您可以控制这个共享对象的编译方式,则可能值得try global-dynamic.

Linux相关问答推荐

Linux/gcc中的文件创建时间系统调用

获取Perl文件::Tail开始在最后流传输文件

无法分析nasm中的单词

boost-iostreams 1.59 sparc-solaris 交叉编译失败

使用sed命令将记录中的字符串替换为DD-MMM-YYYY日期格式

CMake:处理静态库和共享库的正确方法

linux shell获取多文件交集

如果 bash 中已经存在文件名,则创建新文件但添加数字

使用 gdb 将地址转换为行

如何克隆 OpenLDAP 数据库

SVN 错误:无法将字符串从本机编码转换为UTF-8

谁决定任何数据类型或 struct 的大小(取决于 32 位或 64 位)?

如何从命令行打开 Ubuntu Linux 上的 AVD 管理器?

如何搜索文件并将它们压缩到一个 zip 文件中

命令列出除 . (点)和..(点点)

Supervisord - 将进程标准输出重定向到控制台

如何使用 C++ 在 Linux 中获取总 CPU 使用率

比较文件的日期 bash

Vim 增量搜索

如何编写 Mono 守护程序