1

如何让动态加载器为需要版本信息的库/可执行文件加载没有版本信息的库?

例如,假设我正在尝试运行/bin/bash它需要符号 S 和版本 XYZ 并libtinfo.so.6提供符号 S,但由于是使用 musl 工具链构建的,因此没有版本信息。目前,这给了我以下错误:

/bin/bash: /usr/local/x86_64-linux-musl/lib/libtinfo.so.6: no version information available (required by /bin/bash)
Inconsistency detected by ld.so: dl-lookup.c: 112: check_match: Assertion `version->filename == NULL || ! _dl_name_match_p (version->filename, map)' failed!

我试图避免这里描述的过程,在该过程中我制作了一个自定义 DSO,它基本上将所有符号(即我必须写出每个符号)映射到 musl 库中的适当符号。我已经看到很多关于在 DSO 中加载旧版本符号的讨论,但没有关于没有符号版本的讨论。

这是否需要我重新编译所有带有版本符号的二进制文件,以便它们不包含版本信息?

谢谢你的帮助!

更新

经过一番调查,我发现它/bin/bash有一些符号,libtinfo.so.6例如tgoto, tgetstr, tputs, tgetent, tgetflag, tgetnum, UP,BC,PC. tputs@NCURSES6_TINFO_5.0.19991023当动态加载器试图在 musl-built中找到这些符号的正确版本(例如, )时,libtinfo.so.6它会失败,因为该文件中没有版本信息。

我认为我已经开始使用 hack-y 解决方案(希望有更好的解决方案)。本质上,我制作了一个 DSO,我使用 GNU 工具链编译并加载LD_PRELOAD. 在这个 DSO 中,我打开了 musl-built libtinfo.so.6.1withdlopen并使用dlsym来获取所需的符号。然后,这些符号将在全球范围内可用。虽然没有版本信息libtinfo.so.6,但有版本部分(.gnu.version.gnu.version_r),我能够执行 bash 而不会出现任何错误/警告。DSO 来源如下:

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>

/* Functions */
static char *(*tgoto_internal)(const char *string, int x, int y);
static char *(*tgetstr_internal)(const char * id, char **area);
static int (*tputs_internal)(const char *string, int affcnt, int (*outc)(int));
static int (*tgetent_internal)(char *bufp, const char *name);
static int (*tgetflag_internal)(const char *id);
static int (*tgetnum_internal)(const char *id);

void __attribute__ ((constructor)) init(void);

/* Library Constructor */
void
init(void)
{
    void *handle = dlopen("/usr/local/x86_64-linux-musl/lib/libtinfo.so.6.1", RTLD_LAZY);

    tgoto_internal = dlsym(handle, "tgoto");
    tgetstr_internal = dlsym(handle, "tgetstr");
    tputs_internal = dlsym(handle, "tputs");
    tgetent_internal = dlsym(handle, "tgetent");
    tgetflag_internal = dlsym(handle, "tgetflag");
    tgetnum_internal = dlsym(handle, "tgetnum");
}

char *
tgoto(const char *string, int x, int y)
{
    return tgoto_internal(string, x, y);
}

char *
tgetstr(const char * id, char **area)
{
    return tgetstr_internal(id, area);
}

int 
tputs(const char *string, int affcnt, int (*outc)(int))
{
    return tputs_internal(string, affcnt, outc);
}

int 
tgetent(char *bufp, const char *name)
{
    return tgetent_internal(bufp, name);
}

int
tgetflag(const char *id)
{
    return tgetflag_internal(id);
}

int
tgetnum(const char *id)
{
    return tgetnum_internal(id);
}

/* Objects */
char * UP = 0;
char * BC = 0;
char PC = 0;

然而,这个解决方案似乎并不总是有效,在测试 musl 构建的二进制文件时,我仍然看到与上面相同的警告,但这一次,它们不会使测试崩溃,只是打印一个警告。


还应该注意的是,libreadline.solibtinfo.so. 这似乎源于我的 musl-builtlibreadline.so版本错误(8 而不是 7),因此我的配置脚本转到了版本 7 的 GNU ,这试图引入引发错误libreadline.so的 musl 。libtinfo.so使用 musl 工具链构建libreadline.so.7完美地解决了这个错误。

4

1 回答 1

2

感谢@LorinczyZsigmond 帮助我找到解决方案!由于他们不想发布完整的答案,因此我将关闭该问题。

错误:

/bin/bash: /usr/local/x86_64-linux-musl/lib/libtinfo.so.6: no version information available (required by /bin/bash)
Inconsistency detected by ld.so: dl-lookup.c: 112: check_match: Assertion `version->filename == NULL || ! _dl_name_match_p (version->filename, map)' failed!

告诉我们在 musl目录中/bin/bash查找。但是,如果我们看一下,我们会发现它通常会在 GNU 的目录中查找 DSO:libtinfo.so.6lib/bin/bashlddlib

$ ldd /bin/bash
    linux-vdso.so.1 (0x00007ffd485f7000)
    libtinfo.so.6 => /lib/x86_64-linux-gnu/libtinfo.so.6 (0x00007f58ad8ba000)
    libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f58ad8b5000)
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f58ad6f4000)
    /lib64/ld-linux-x86-64.so.2 => //lib64/ld-linux-x86-64.so.2 (0x00007f58ada22000)

/bin/bash运行并且LD_LIBRARY_PATH环境变量指向 musllib目录时,加载器将尝试libtinfo.so.6使用 musl 解决依赖关系libtinfo.so.6,而不是 GNU 的。这会导致冲突,因为它与具有符号版本控制甚至更多/bin/bash的 GNU 相关联。libtinfo.so.6

正如@LorincyZsigmond 所说,修复方法是:

本地编译的共享对象应首先由本地编译的程序搜索,但对“默认”程序隐藏。

所以本质上我不需要混合 GNU 和 musl 库,而我是通过强硬设置来做的LD_LIBRARY_PATH=/usr/local/x86_64-linux-musl/lib

LD_LIBRARY_PATH我没有使用,而是使用rpath链接器选项 ( -L/usr/local/x86_64-linux-musl/lib -Wl,-rpath,/usr/local/x86_64-linux-musl/lib) 将我的 musl 库的路径硬编码到可执行文件中。这允许 musl 构建的二进制文件链接到 DSO 的需要,同时还允许 GNU 构建的二进制文件链接到 GNU 构建的 DSO(在执行诸如测试从源代码构建的 vim 之类的事情时,这两者都是必需的)。

顺便说一句:rpath首先搜索 ELF 动态部分中的条目。

于 2020-11-24T18:34:50.920 回答