【问题标题】:Can a dynamically linked glibc application dlopen() a static linked musl shared object?动态链接的 glibc 应用程序 dlopen() 可以静态链接的 musl 共享对象吗?
【发布时间】:2020-10-28 09:25:49
【问题描述】:

我有一个当前与glibc 动态链接的库。 这个库动态加载到一个应用程序中,该应用程序也动态链接到glibc。我无法控制应用程序,只能控制共享对象。

但是,有时加载库会导致应用程序获取SIGKILLd,因为它具有非常严格的实时要求并且相应地设置了rlimits。用分析器查看这个告诉我,大部分时间实际上都花在了链接器上。所以本质上动态链接实际上太慢了(有时)。嗯,这不是我曾经想过的问题:)

我希望通过生成一个静态链接的共享对象来解决这个问题。但是,谷歌搜索这个问题并阅读多个其他 SO 线程警告我不要尝试静态链接 glibc。但这些似乎是 glibc 特有的问题。

所以我的问题是,如果我将这个共享库与musl 静态链接,然后让一个(动态链接的)glibc 应用程序dlopen 它,那会安全吗?多个libc一般有问题吗?

【问题讨论】:

    标签: gcc linker glibc musl


    【解决方案1】:

    用分析器查看这个告诉我,大部分时间实际上都花在了链接器上。

    您的分析方法非常出了点问题。

    首先,“链接器”在应用程序运行时不会运行,只有 loader(又名 rtld,又名 ld-linux)会运行。我假设您的意思不是加载器,而不是链接器。

    其次,加载器确实有一些运行时成本在启动时,但是由于您调用的每个函数都只解析一次,因此加载器运行时成本的比例在运行任何可感知时间(超过大约 1 分钟)的应用程序应该很快接近零。

    所以本质上动态链接实际上太慢了(有时)。

    您可以通过使用-Wl,-z,now 链接器标志链接,让加载程序在加载时解析共享库中的所有动态符号。

    如果我将这个共享库静态链接到 musl,然后让(动态链接的)glibc 应用程序 dlopen 它,那会安全吗?

    这不仅不安全,而且很可能根本不起作用(除了大多数琐碎的共享库)。

    是否存在多个 libc 的一般问题?

    将多个 libc 链接到一个进程中会导致太多问题。

    更新:

    在加载时解析所有符号与我想要的完全相反,因为进程在加载共享对象期间被终止,之后它运行正常。

    听起来你正在使用dlopen进程已经执行时间关键的实时任务。

    这不是明智之举:dlopen(除其他外)调用malloc,从磁盘读取数据,执行mmap 调用等等。所有需要锁,并且可以等待任意长的时间。

    通常的解决方案是让应用程序在进入时间关键循环之前执行初始化(加载您的库将是其中的一部分)。

    由于您无法控制应用程序,您唯一能做的就是告诉应用程序开发人员他们当前的需求(如果这些实际上是他们的需求)无法满足 - 他们必须提供一些方法来执行初始化在进入时间关键部分之前,否则他们将总是SIGKILL的风险。加快您的库加载速度只会使SIGKILL 出现频率较低,但不会完全删除它。

    更新 2:

    是的,我知道我能做的最好的事情就是降低频率而不是“解决”问题,只是尝试减轻它。

    您应该查看prelink。它可以显着降低执行重定位所需的时间。这并不能保证您选择的预链接地址将可用,因此您有时可能仍会收到SIGKILLed,但这可能是一种有效的缓解措施。

    【讨论】:

    • 是的,我的意思是装载机,对不起。也许我误解了一些东西,但是在加载时解析所有符号与我想要的完全相反,因为在加载共享对象期间进程会被 sigkilled,之后它运行良好。问题是dlopen() 有时会花费太多时间并导致主机进程无法满足它的实时要求,以内核SIGKILLing 结尾。我不明白这将如何帮助我减少时间。但是,我可能会查看 RTLD_LAZY,因此我现在找到了它。
    • 谢谢,是的,我知道我能做的最好的事情就是降低频率而不是“解决”问题,而只是尝试减轻它。这就是我在这里想要做的。
    • @henry 更新 2.
    • "将多个 libc 链接到一个进程中会导致太多问题无法计算。" -- @EmployedRussian 这可以使用导出映射来有效地保持两个 libc 库独立。这是您可以用来管理多个版本的 libstdc++ 的方法(请参阅here)。我很好奇是否可以将相同的方法应用于 musl 的 libc。
    【解决方案2】:

    理论上可以做这样的事情,但是你必须编写一个新版本的 musl 启动代码来处理线程指针和 TCB 已经由 glibc 设置的事实,并运行该代码来自共享对象中的 ELF 构造函数。由于 TCB 布局差异,某些 musl 功能将不可用。

    我认为这不太可能解决您的实际问题。即使它与时间相关,这种 hack 也有可能使事情变得更糟,因为它增加了所需的运行时重定位量。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-05-09
      • 1970-01-01
      • 2017-11-26
      • 2013-01-12
      • 1970-01-01
      • 2010-11-17
      相关资源
      最近更新 更多