MIPSEL GLIBC sem_init() 未共享

MIPSEL GLIBC sem_init() not shared

我目前正在为 MIPSEL 平台 在 Linux 上开发 运行 应用程序。 该应用程序在其一个 DSO 中使用 POSIX semaphores,在我的案例中,注意力集中在应用程序所依赖的一个 DSO 中使用的 sem_init() 函数。

该应用程序当前正在使用 GLIBC_2.31,但我在使用 GLIBC_2_26[ 时也观察到了同样的现象=71=].

下面我将解释异常情况和目前排查的状态。

当我注意到等待信号量的进程在信号量递增其值时不会唤醒时,这一切就发生了。

仅作记录,我知道对于多处理,信号量必须位于使用信号量的进程之间共享的内存区域中。

现在,进一步调查使用 strace,我还注意到我使用 sem_init(&semaphore, 1, 0) 初始化的信号量导致 private ,尽管我将其初始化为共享。

futex(0x774ce0bc, FUTEX_WAIT_BITSET_PRIVATE|FUTEX_CLOCK_REALTIME, 1, {tv_sec=1615204364, tv_nsec=515578961}, FUTEX_BITSET_MATCH_ANY) = -1 ETIMEDOUT (Connection timed out)

我在这个问题上执行的下一步是使用 gdb 调试信号量初始化过程。在那里我发现在这个特定的组合中,MIPSEL - LINUX - GLIBCsem_init() 存在两个版本:

$ mipsel-linux-objdump -T libpthread.so.0  | grep sem_init
00012e30 g    DF .text  00000040 (GLIBC_2.0)  sem_init
00012dd0 g    DF .text  00000060  GLIBC_2.2   sem_init

出于某种原因,linker 决定 link 我的代码针对 旧的 (不支持共享信号量)而不是 更新.

查看 GLIBC 代码,特别是 sem_init() 实现的地方 (/nptl/sem_init.c),我意识到 sem_init() 是一个别名,两个符号标识函数'明确实施。

然后我的下一步是直接调用我需要的函数 __new_sem_init(),只是为了确保我不知道的任何 linker 炼金术会干扰我的意图。

很遗憾,该符号未导出,无法使用。

查看我的库 liba.so,它使用 sem_wait()sem_init()sem_post(),我注意到这些符号都是由 libpthread.so.0 导出的.

$ mipsel-linux-nm -D libpthread.so.0 | grep sem_
00014a10 T sem_clockwait
00013784 T sem_close
00012e70 T sem_destroy
00012e70 T sem_destroy
00013b14 T sem_getvalue
00013b00 T sem_getvalue
00012e30 T sem_init
00012dd0 T sem_init
000131b8 T sem_open
00014ab0 T sem_post
00014b90 T sem_post
00014508 T sem_timedwait
00013f40 T sem_trywait
00013fac T sem_trywait
00013920 T sem_unlink
00013eb0 T sem_wait
00014020 T sem_wait

但是查看 liba.so 依赖项,我惊讶于该库仅依赖于主 libc 库 (libc.so.6) 并且不依赖于 libpthread.so.0 实现了实际的 sem_* 符号。

$ /lib/ld-2.31.so --list /usr/lib/liba.so 
        linux-vdso.so.1 (0x7ffaa000)
        libc.so.6 => /lib/libc.so.6 (0x77ddc000)
        /lib/ld-2.31.so (0x77f8a000)

我想这可能与我的问题密切相关,但我不知道如何 link 这两个事实。 liba.so 如何使用其依赖项不提供的符号进行编译和 linked?

$ mipsel-linux-objdump -T libc.so.6  | grep sem
000fab80 g    DF .text  00000070  GLIBC_2.0   semget
000fabf0 g    DF .text  000000b0  GLIBC_2.2   semctl
000faca0  w   DF .text  00000074  GLIBC_2.3.3 semtimedop
000359d0 g    DF .text  00000074  GLIBC_2.0   sigisemptyset
00149854 g    DF .text  000000b0 (GLIBC_2.0)  semctl
000fab60 g    DF .text  00000018  GLIBC_2.0   semop
$

如何强制 GNU linker link 反对 sem_init@@GLIBC_2.2 而不是 sem_init@GLIBC_2.0

看来我的问题确实是下link问题

经过对文献的适当研究和大量测试,我终于能够解决我原来的问题。让我的应用程序使用 sem_init().

的正确版本

我的假设是因为我的库没有携带 libpthread 依赖项是正确的路径。

检查我的 Makefile,我发现创建共享库的语句中缺少 -libpthread 开关。

这就是问题的根源,添加开关后,一切都按预期开始工作。

延迟实现解决方案的其中一件事是在不同的目标上使用相同的 Makefile 构建相同的库; libpthread 依赖项已包含在库中。

但我没有任何解释为什么动态 linker 选择最旧的符号 sem_init@GLIBC_2_0 代替 sem_init@GLIBC_2_2

我知道 linker 能够 link 我的 sem_init() 因为原始可执行文件 applibpthread 作为依赖项,所以在link 时间,图书馆在那里,它可以找到符号。

我无法解释的是为什么它选择了 sem_init@GLIBC_2_0

根据这些来源

http://peeterjoot.com/2019/09/20/an-example-of-linux-glibc-symbol-versioning/ The @@ one means that it applies to new code, whereas the @MYSTUFF_1.1 is a load only function, and no new code can use that symbol.

https://developers.redhat.com/blog/2019/08/01/how-the-gnu-c-library-handles-backward-compatibility/ The @@ tells the dynamic linker that this version is the default version.

https://web.archive.org/web/20100430151127/http://www.trevorpounds.com/blog/?33 The double @@ can only be defined once for a given symbol since it denotes the default version to use.

如果符号未版本化,则带有双 @@ 的符号应由动态 linker 选择。

仅当符号未版本化并且需要该符号的对象正确地包含对包含该符号的 DSO 的依赖项时,此陈述才正确吗?