如何让动态加载程序加载一个没有版本控制信息的库,而该库/可执行文件需要版本控制信息?
例如,假设我正在尝试运行/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!
我试图避免这里描述的过程,即我制作一个自定义DSO,它基本上将所有符号(即我必须写出每个符号(映射到musl库中的适当符号。我看过很多关于在DSO中加载旧版本符号的讨论,但没有看到关于无符号版本的讨论。
这是否需要我重新编译所有带有版本化符号的二进制文件,这样它们就不会包含版本化信息?
谢谢你的帮助!
更新
经过研究,我发现/bin/bash
有一些从libtinfo.so.6
得到的符号,如tgoto
、tgetstr
、tputs
、tgetent
、tgetflag
、tgetnum
、UP
、BC,
和PC
。当动态加载程序试图在musl构建的libtinfo.so.6
中找到这些符号的正确版本(例如,tputs@NCURSES6_TINFO_5.0.19991023
(时,它会失败,因为该文件中没有版本控制信息。
我想我已经有了一个破解解决方案的开端(希望有更好的解决方案(。从本质上讲,我制作了一个DSO,用GNU工具链编译并用LD_PRELOAD
加载。在这个DSO中,我用dlopen
打开musl构建的libtinfo.so.6.1
,并使用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.so
中查找libtinfo.so
中的版本控制信息时遇到过类似的版本控制错误。这似乎源于我的musl构建的libreadline.so
是错误的版本(8而不是7(,因此我的配置脚本转到了GNUlibreadline.so
,它是版本7,这试图引入引发错误的musllibtinfo.so
。使用musl工具链构建libreadline.so.7
完美地解决了这个错误。
感谢@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!
告诉我们/bin/bash
正在musllib
目录中查找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
目录时,加载程序将尝试使用musl的libtinfo.so.6
而不是GNU来解决libtinfo.so.6
依赖关系。这导致了冲突,因为/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时需要这两种功能(。
顺便说一句:首先搜索ELF动态部分中的rpath
条目。