3

背景

我遇到了一个问题,它违反了我的位置无关代码和线程本地存储的概念模型。提示此问题的问题可以在此 StackOverflow 帖子中找到;我有一个二进制文件,它又dlopen是一个共享对象。打开共享对象会触发错误声明dlopen: cannot load any more object with static TLS

我对此的理解是,该initial-exec模型被称为“静态 TLS”,并且在不创建与位置无关的代码时,这通常是默认设置。当创建与位置无关的代码时,默认值通常是其他东西,例如global-dynamicGCC 使用的模型。我认为这是因为initial-exec无法在共享对象中工作。对另一个 StackOverflow 帖子的回答支持了这一信念,并指出:

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

鉴于我在 x86_64 机器上,这导致了一些混乱。然后我遇到了另一个 StackOverflow 问题,答案似乎是使用静态 TLS 模型创建共享对象。

看到这一点,我决定返回我有问题的二进制文件并递归扫描依赖项以使用静态 TLS 模型,方法readelf -d是根据这个问题的答案查看输出。令我惊讶的是,我发现了一些图书馆。令我沮丧的是,它们不是应用程序构建的库。

这是readelf -d其中之一的输出:

/lib64/libpthread.so.0

Dynamic section at offset 0x17d90 contains 29 entries:
  Tag        Type                         Name/Value
 0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]
 0x0000000000000001 (NEEDED)             Shared library: [ld-linux-x86-64.so.2]
 0x000000000000000e (SONAME)             Library soname: [libpthread.so.0]
 0x000000000000000c (INIT)               0x38c46052d0
 0x000000000000000d (FINI)               0x38c4611120
 0x0000000000000004 (HASH)               0x38c4615e90
 0x000000006ffffef5 (GNU_HASH)           0x38c4600280
 0x0000000000000005 (STRTAB)             0x38c4602dd8
 0x0000000000000006 (SYMTAB)             0x38c4600f00
 0x000000000000000a (STRSZ)              4918 (bytes)
 0x000000000000000b (SYMENT)             24 (bytes)
 0x0000000000000003 (PLTGOT)             0x38c4817fe8
 0x0000000000000002 (PLTRELSZ)           1680 (bytes)
 0x0000000000000014 (PLTREL)             RELA
 0x0000000000000017 (JMPREL)             0x38c4604c38
 0x0000000000000007 (RELA)               0x38c4604578
 0x0000000000000008 (RELASZ)             1728 (bytes)
 0x0000000000000009 (RELAENT)            24 (bytes)
 0x000000006ffffffc (VERDEF)             0x38c46043a0
 0x000000006ffffffd (VERDEFNUM)          10
 0x000000000000001e (FLAGS)              STATIC_TLS
 0x000000006ffffffb (FLAGS_1)            Flags: NODELETE INITFIRST
 0x000000006ffffffe (VERNEED)            0x38c46044f8
 0x000000006fffffff (VERNEEDNUM)         2
 0x000000006ffffff0 (VERSYM)             0x38c460410e
 0x000000006ffffff9 (RELACOUNT)          60
 0x000000006ffffdf8 (CHECKSUM)           0x86f709c8
 0x000000006ffffdf5 (GNU_PRELINKED)      2018-05-23T11:25:00
 0x0000000000000000 (NULL)               0x0

在这里我们可以看到STATIC_TLS,这使我相信该initial-exec模型已被使用。

的输出readelf -l

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

Program Headers:
  Type           Offset             VirtAddr           PhysAddr
                 FileSiz            MemSiz              Flags  Align
  PHDR           0x0000000000000040 0x00000038c4600040 0x00000038c4600040
                 0x00000000000001f8 0x00000000000001f8  R E    8
  INTERP         0x0000000000011830 0x00000038c4611830 0x00000038c4611830
                 0x000000000000001c 0x000000000000001c  R      10
      [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
  LOAD           0x0000000000000000 0x00000038c4600000 0x00000038c4600000
                 0x0000000000016df0 0x0000000000016df0  R E    200000
  LOAD           0x0000000000017b90 0x00000038c4817b90 0x00000038c4817b90
                 0x00000000000006e0 0x0000000000004860  RW     200000
  DYNAMIC        0x0000000000017d90 0x00000038c4817d90 0x00000038c4817d90
                 0x00000000000001f0 0x00000000000001f0  RW     8
  NOTE           0x0000000000000238 0x00000038c4600238 0x00000038c4600238
                 0x0000000000000044 0x0000000000000044  R      4
  GNU_EH_FRAME   0x000000000001184c 0x00000038c461184c 0x00000038c461184c
                 0x0000000000000a5c 0x0000000000000a5c  R      4
  GNU_STACK      0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x0000000000000000 0x0000000000000000  RW     8
  GNU_RELRO      0x0000000000017b90 0x00000038c4817b90 0x00000038c4817b90
                 0x0000000000000470 0x0000000000000470  R      1

 Section to Segment mapping:
  Segment Sections...
   00     
   01     .interp 
   02     .note.gnu.build-id .note.ABI-tag .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_d .gnu.version_r .rela.dyn .rela.plt .init .plt .text __libc_freeres_fn .fini .rodata .interp .eh_frame_hdr .eh_frame .gcc_except_table .hash 
   03     .ctors .dtors .jcr .data.rel.ro .dynamic .got .got.plt .data .bss 
   04     .dynamic 
   05     .note.gnu.build-id .note.ABI-tag 
   06     .eh_frame_hdr 
   07     
   08     .ctors .dtors .jcr .data.rel.ro .dynamic .got 

我对这里缺少 TLS 部分感到惊讶,但即便如此,我们也有明确的迹象表明共享对象正在使用静态initial-execTLS 模型。

最后,我看到有类似问题的人重新排序依赖项以摆脱早期的dlopen错误。我不是为什么这会有所作为。

问题)

  • initial-exec可重定位代码(尤其是 x86_64 上的共享对象)中的函数如何?
  • 为什么重新排序依赖项有时会解决dlopen问题;使用的插槽数量肯定保持不变吗?

dlopen也欢迎对原始问题提出任何其他建议。

更新 1

在进一步研究这个问题的同时,我发现了另一个来源,指出静态 TLS 模型不能在共享库中使用:

DF_STATIC_TLS
    If set in a shared object or executable, this flag instructs the dynamic linker to reject attempts to load this file dynamically. It indicates that the shared object or executable contains code using a static thread-local storage scheme. Implementations need not support any form of thread-local storage.
4

0 回答 0