如何加载没有版本控制信息的 DSO?
How can I load a DSO with no versioning information?
如何让动态加载程序为需要版本信息的 library/executable 加载没有版本信息的库?
例如,假设我正在尝试 运行 /bin/bash
,它需要版本为 X.Y.Z 的符号 S,而 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!
我试图避免 described here 我制作自定义 DSO 的过程,该过程基本上将所有符号(即我必须写出每个符号)映射到 musl 库中的适当符号。我看到很多关于在 DSO 中加载旧版本符号的讨论,但没有关于 NO 符号版本的讨论。
这是否需要我重新编译所有带有版本符号的二进制文件,以便它们不包含版本信息?
感谢您的帮助!
更新
经过一些调查,我发现 /bin/bash
有一些从 libtinfo.so.6
得到的符号,例如 tgoto
、tgetstr
、tputs
、tgetent
、tgetflag
、tgetnum
、UP
、BC,
和 PC
。当动态加载程序试图在 musl 构建的 libtinfo.so.6
中找到这些符号的正确版本(例如,tputs@NCURSES6_TINFO_5.0.19991023
)时,它失败了,因为该文件中没有版本控制信息。
我想我已经开始使用 hack-y 解决方案(希望有更好的解决方案)。本质上,我制作了一个 DSO,我使用 GNU 工具链编译并使用 LD_PRELOAD
加载。在这个 DSO 中,我用 dlopen
打开 musl 构建的 libtinfo.so.6.1
并使用 dlsym
来获取所需的符号。这些符号然后在全球范围内可用。虽然 libtinfo.so.6
没有版本信息,但有版本部分(.gnu.version
和 .gnu.version_r
),我可以在没有任何 errors/warning 的情况下执行 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.so
中查找 libtinfo.so
中的版本信息时遇到了类似的版本错误。这似乎是因为我的 musl-built libreadline.so
是错误的版本(8 而不是 7),因此我的配置脚本转到了版本 7 的 GNU libreadline.so
,这试图引入musl libtinfo.so
引发了错误。使用 musl 工具链构建 libreadline.so.7
完美地解决了这个错误。
感谢@LorinczyZsigmond 帮助我找到解决方案!由于他们不想 post 一个完整的答案,我将关闭这个问题。
错误:
/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!
告诉我们 /bin/bash
正在 musl lib
目录中寻找 libtinfo.so.6
。但是,如果我们查看 ldd
下的 /bin/bash
,我们会发现通常它会在 GNU 的 lib
目录中查找 DSO:
$ 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
目录时,loader会尝试解析libtinfo.so.6
依赖使用 musl 的 libtinfo.so.6
,而不是 GNU 的。这会导致冲突,因为 /bin/bash
是针对 GNU 的 libtinfo.so.6
编辑的 libtinfo.so.6
,后者具有符号版本控制甚至更多。
正如@LorincyZsigmond 所说,解决方法是:
locally compiled shared objects should be searched first by locally compiled programs, but be hidden from the 'default' programs.
所以基本上我不需要混合 GNU 和 musl 库,这是我通过粗暴设置 LD_LIBRARY_PATH=/usr/local/x86_64-linux-musl/lib
。
我没有使用 LD_LIBRARY_PATH
,而是使用了 rpath
linker 选项 (-L/usr/local/x86_64-linux-musl/lib -Wl,-rpath,/usr/local/x86_64-linux-musl/lib
) 将我的 musl 库的路径硬编码到可执行文件中。这允许 musl 构建的二进制文件 link 反对 DSO 的需要,同时也允许 GNU 构建的二进制文件 link 反对 GNU 构建的 DSO(在做测试 [=50 之类的事情时两者都是必需的) =] 从源代码构建)。
顺便说一句:首先搜索 ELF 动态部分中的 rpath
个条目。
如何让动态加载程序为需要版本信息的 library/executable 加载没有版本信息的库?
例如,假设我正在尝试 运行 /bin/bash
,它需要版本为 X.Y.Z 的符号 S,而 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!
我试图避免 described here 我制作自定义 DSO 的过程,该过程基本上将所有符号(即我必须写出每个符号)映射到 musl 库中的适当符号。我看到很多关于在 DSO 中加载旧版本符号的讨论,但没有关于 NO 符号版本的讨论。
这是否需要我重新编译所有带有版本符号的二进制文件,以便它们不包含版本信息?
感谢您的帮助!
更新
经过一些调查,我发现 /bin/bash
有一些从 libtinfo.so.6
得到的符号,例如 tgoto
、tgetstr
、tputs
、tgetent
、tgetflag
、tgetnum
、UP
、BC,
和 PC
。当动态加载程序试图在 musl 构建的 libtinfo.so.6
中找到这些符号的正确版本(例如,tputs@NCURSES6_TINFO_5.0.19991023
)时,它失败了,因为该文件中没有版本控制信息。
我想我已经开始使用 hack-y 解决方案(希望有更好的解决方案)。本质上,我制作了一个 DSO,我使用 GNU 工具链编译并使用 LD_PRELOAD
加载。在这个 DSO 中,我用 dlopen
打开 musl 构建的 libtinfo.so.6.1
并使用 dlsym
来获取所需的符号。这些符号然后在全球范围内可用。虽然 libtinfo.so.6
没有版本信息,但有版本部分(.gnu.version
和 .gnu.version_r
),我可以在没有任何 errors/warning 的情况下执行 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.so
中查找 libtinfo.so
中的版本信息时遇到了类似的版本错误。这似乎是因为我的 musl-built libreadline.so
是错误的版本(8 而不是 7),因此我的配置脚本转到了版本 7 的 GNU libreadline.so
,这试图引入musl libtinfo.so
引发了错误。使用 musl 工具链构建 libreadline.so.7
完美地解决了这个错误。
感谢@LorinczyZsigmond 帮助我找到解决方案!由于他们不想 post 一个完整的答案,我将关闭这个问题。
错误:
/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!
告诉我们 /bin/bash
正在 musl lib
目录中寻找 libtinfo.so.6
。但是,如果我们查看 ldd
下的 /bin/bash
,我们会发现通常它会在 GNU 的 lib
目录中查找 DSO:
$ 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
目录时,loader会尝试解析libtinfo.so.6
依赖使用 musl 的 libtinfo.so.6
,而不是 GNU 的。这会导致冲突,因为 /bin/bash
是针对 GNU 的 libtinfo.so.6
编辑的 libtinfo.so.6
,后者具有符号版本控制甚至更多。
正如@LorincyZsigmond 所说,解决方法是:
locally compiled shared objects should be searched first by locally compiled programs, but be hidden from the 'default' programs.
所以基本上我不需要混合 GNU 和 musl 库,这是我通过粗暴设置 LD_LIBRARY_PATH=/usr/local/x86_64-linux-musl/lib
。
我没有使用 LD_LIBRARY_PATH
,而是使用了 rpath
linker 选项 (-L/usr/local/x86_64-linux-musl/lib -Wl,-rpath,/usr/local/x86_64-linux-musl/lib
) 将我的 musl 库的路径硬编码到可执行文件中。这允许 musl 构建的二进制文件 link 反对 DSO 的需要,同时也允许 GNU 构建的二进制文件 link 反对 GNU 构建的 DSO(在做测试 [=50 之类的事情时两者都是必需的) =] 从源代码构建)。
顺便说一句:首先搜索 ELF 动态部分中的 rpath
个条目。