用 LD_PRELOAD 替换 memcpy@glibc_2.14
provide replacement for memcpy@glibc_2.14 by LD_PRELOAD
是我们的老朋友"version `GLIBC_2.14' not found"。客户实际上需要使用仅提供 glibc 2.11 版本的相当旧的 Linux。我被预编译库困住了。
Linus LD_PRELOAD workaround 根本不起作用。我想,这是因为我的库明确要求 memcpy@GLIBC_2.14
.
所以我尝试了另一种方法。首先,find the function that needs the newer version.
$ export LD_LIBRARY_PATH=/home/kremers/experiment/lib
$ /home/kremers/experiment/runner
/home/kremers/experiment/runner: /usr/lib64/libstdc++.so.6: version `GLIBCXX_3.4.15' not found (required by /home/kremers/experiment/runner)
/home/kremers/experiment/runner: /lib64/libc.so.6: version `GLIBC_2.14' not found (required by /home/kremers/experiment/lib/libfoo.so)
现在,我试图从 GLIBC_2.14
中找到所需的确切符号
$ readelf -s /home/kremers/experiment/lib/libfoo.so | grep GLIBC_2.14
174: 0000000000000000 0 FUNC GLOBAL DEFAULT UND memcpy@GLIBC_2.14 (12)
1242: 0000000000000000 0 FUNC GLOBAL DEFAULT UND memcpy@@GLIBC_2.14
我很幸运。才memcpy
!所以我尝试构建自己的 memcpy@GLIBC_2.14
,类似于 [
memcpy.c:
/* this was taken from https://bugzilla.redhat.com/show_bug.cgi?id=638477#c55
* */
#include <sys/types.h>
void *memcpy(void *dst, const void *src, size_t size)
{
void *orig = dst;
asm volatile("rep ; movsq"
:"=D" (dst), "=S" (src)
:"0" (dst), "1" (src), "c" (size >> 3)
:"memory");
asm volatile("rep ; movsb"
:"=D" (dst), "=S" (src)
:"0" (dst), "1" (src), "c" (size & 7)
:"memory");
return orig;
}
memcpy.map:
GLIBC_2.14 {
memcpy;
};
我从中构建了一个共享库:
$ gcc -shared -fPIC -fno-builtin -c memcpy.c
$ gcc -shared -fPIC -Wl,--version-script memcpy.map -o libmemcpy-2.14.so memcpy.o -lc
$ cp libmemcpy-2.14.so ../lib/.
检查是否存在所需的符号:
$ readelf -sW /home/kremers/experiment/lib/libmemcpy-2.14.so | grep GLIBC
4: 0000000000000000 0 FUNC WEAK DEFAULT UND __cxa_finalize@GLIBC_2.2.5 (3)
5: 0000000000000000 0 OBJECT GLOBAL DEFAULT ABS GLIBC_2.14
10: 000000000000061c 112 FUNC GLOBAL DEFAULT 13 memcpy@@GLIBC_2.14
53: 0000000000000000 0 OBJECT GLOBAL DEFAULT ABS GLIBC_2.14
54: 0000000000000000 0 FUNC WEAK DEFAULT UND __cxa_finalize@@GLIBC_2.2.5
$ readelf -s /home/kremers/experiment/lib/libfoo.so | grep GLIBC_2.14
174: 0000000000000000 0 FUNC GLOBAL DEFAULT UND memcpy@GLIBC_2.14 (12)
1242: 0000000000000000 0 FUNC GLOBAL DEFAULT UND memcpy@@GLIBC_2.14
请注意 libfoo 需要 memcpy@GLIBC_2.14
和 memcpy@@GLIBC_2.14
的区别。 libmemcpy
只提供 memcpy@GLIBC_2.14
。而且我不知道如何更改它。
$ export LD_LIBRARY_PATH=/home/kremers/experiment/lib
$ export LD_PRELOAD=/home/kremers/experiment/lib/libmemcpy-2.14.so
$ /home/kremers/experiment/runner
/home/kremers/experiment/runner: /usr/lib64/libstdc++.so.6: version `GLIBCXX_3.4.15' not found (required by /home/kremers/experiment/runner)
/home/kremers/experiment/runner: /lib64/libc.so.6: version `GLIBC_2.14' not found (required by /home/kremers/experiment/lib/libfoo.so)
似乎没有任何变化。我只能验证 libmemcpy
是否已加载:
$ ldd lib/libfoo.so
lib/libfoo.so: /lib64/libc.so.6: version `GLIBC_2.14' not found (required by lib/libfoo.so)
lib/libfoo.so: /usr/lib64/libstdc++.so.6: version `GLIBCXX_3.4.15' not found (required by lib/libfoo.so)
linux-vdso.so.1 => (0x00007fff46d99000)
/home/kremers/experiment/lib/libmemcpy-2.14.so (0x00007fada31f7000)
libstdc++.so.6 => /usr/lib64/libstdc++.so.6 (0x00007fada2ee5000)
libm.so.6 => /lib64/libm.so.6 (0x00007fada2c8e000)
libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007fada2a78000)
libc.so.6 => /lib64/libc.so.6 (0x00007fada270a000)
/lib64/ld-linux-x86-64.so.2 (0x00007fada3669000)
结论
我的印象是,这应该可行,但我遗漏了一个细节。我的第一个猜测是 memcpy@GLIBC_2.14
与 memcpy@@GLIBC_2.14
不匹配。
PS
我很难找到线索,因为 bing 或 google 似乎不支持搜索“@”和“@@”。虽然 readelf
有很多文档,但每个文档只涵盖一小部分。通常会遗漏我要找的东西。因此,如果解决方案在某处有详细记录,请耐心等待。
glibc 动态链接器目前不支持此功能。原来的 GNU symbol versioning specification 需要多次一致性检查,这些检查无法关闭并阻止通过预加载向现有库添加全新的符号版本。
首先,您应该简单地插入一个未版本化的符号。所有符号版本都将绑定到它。由于您可以安排没有可观察到的行为差异(只需调用 memmove
),因此您不需要在此处进行细粒度控制。符号 memcpy@GLIBC_2.14
不会插入任何内容,因此原始规范要求动态链接器检查符号定义的 soname 是否与符号引用中包含的 soname 匹配,并且在预加载时不会匹配。
但这不会解决您的问题,因为另一个一致性检查:由符号版本参考中的 soname 标识的库必须使用此符号版本来定义 any象征。简而言之,符号版本本身的绑定并不懒惰。即使从未使用过符号版本,它也会失败,这可能是由于预加载,也可能是因为实际的符号引用是惰性的且从未绑定。
我们可能应该删除第一个检查(我们 运行 由于它导致 glibc 本身出现问题),并添加一个旋钮来关闭第二个检查。但是您必须将该更改反向移植到您的 2.11 派生的 glibc 中,此时,您可以简单地重建它并为 memcpy
添加一个带有版本符号 memcpy@GLIBC_2.14
的别名。对不起。
是我们的老朋友"version `GLIBC_2.14' not found"。客户实际上需要使用仅提供 glibc 2.11 版本的相当旧的 Linux。我被预编译库困住了。
Linus LD_PRELOAD workaround 根本不起作用。我想,这是因为我的库明确要求 memcpy@GLIBC_2.14
.
所以我尝试了另一种方法。首先,find the function that needs the newer version.
$ export LD_LIBRARY_PATH=/home/kremers/experiment/lib
$ /home/kremers/experiment/runner
/home/kremers/experiment/runner: /usr/lib64/libstdc++.so.6: version `GLIBCXX_3.4.15' not found (required by /home/kremers/experiment/runner)
/home/kremers/experiment/runner: /lib64/libc.so.6: version `GLIBC_2.14' not found (required by /home/kremers/experiment/lib/libfoo.so)
现在,我试图从 GLIBC_2.14
$ readelf -s /home/kremers/experiment/lib/libfoo.so | grep GLIBC_2.14
174: 0000000000000000 0 FUNC GLOBAL DEFAULT UND memcpy@GLIBC_2.14 (12)
1242: 0000000000000000 0 FUNC GLOBAL DEFAULT UND memcpy@@GLIBC_2.14
我很幸运。才memcpy
!所以我尝试构建自己的 memcpy@GLIBC_2.14
,类似于 [
memcpy.c:
/* this was taken from https://bugzilla.redhat.com/show_bug.cgi?id=638477#c55
* */
#include <sys/types.h>
void *memcpy(void *dst, const void *src, size_t size)
{
void *orig = dst;
asm volatile("rep ; movsq"
:"=D" (dst), "=S" (src)
:"0" (dst), "1" (src), "c" (size >> 3)
:"memory");
asm volatile("rep ; movsb"
:"=D" (dst), "=S" (src)
:"0" (dst), "1" (src), "c" (size & 7)
:"memory");
return orig;
}
memcpy.map:
GLIBC_2.14 {
memcpy;
};
我从中构建了一个共享库:
$ gcc -shared -fPIC -fno-builtin -c memcpy.c
$ gcc -shared -fPIC -Wl,--version-script memcpy.map -o libmemcpy-2.14.so memcpy.o -lc
$ cp libmemcpy-2.14.so ../lib/.
检查是否存在所需的符号:
$ readelf -sW /home/kremers/experiment/lib/libmemcpy-2.14.so | grep GLIBC
4: 0000000000000000 0 FUNC WEAK DEFAULT UND __cxa_finalize@GLIBC_2.2.5 (3)
5: 0000000000000000 0 OBJECT GLOBAL DEFAULT ABS GLIBC_2.14
10: 000000000000061c 112 FUNC GLOBAL DEFAULT 13 memcpy@@GLIBC_2.14
53: 0000000000000000 0 OBJECT GLOBAL DEFAULT ABS GLIBC_2.14
54: 0000000000000000 0 FUNC WEAK DEFAULT UND __cxa_finalize@@GLIBC_2.2.5
$ readelf -s /home/kremers/experiment/lib/libfoo.so | grep GLIBC_2.14
174: 0000000000000000 0 FUNC GLOBAL DEFAULT UND memcpy@GLIBC_2.14 (12)
1242: 0000000000000000 0 FUNC GLOBAL DEFAULT UND memcpy@@GLIBC_2.14
请注意 libfoo 需要 memcpy@GLIBC_2.14
和 memcpy@@GLIBC_2.14
的区别。 libmemcpy
只提供 memcpy@GLIBC_2.14
。而且我不知道如何更改它。
$ export LD_LIBRARY_PATH=/home/kremers/experiment/lib
$ export LD_PRELOAD=/home/kremers/experiment/lib/libmemcpy-2.14.so
$ /home/kremers/experiment/runner
/home/kremers/experiment/runner: /usr/lib64/libstdc++.so.6: version `GLIBCXX_3.4.15' not found (required by /home/kremers/experiment/runner)
/home/kremers/experiment/runner: /lib64/libc.so.6: version `GLIBC_2.14' not found (required by /home/kremers/experiment/lib/libfoo.so)
似乎没有任何变化。我只能验证 libmemcpy
是否已加载:
$ ldd lib/libfoo.so
lib/libfoo.so: /lib64/libc.so.6: version `GLIBC_2.14' not found (required by lib/libfoo.so)
lib/libfoo.so: /usr/lib64/libstdc++.so.6: version `GLIBCXX_3.4.15' not found (required by lib/libfoo.so)
linux-vdso.so.1 => (0x00007fff46d99000)
/home/kremers/experiment/lib/libmemcpy-2.14.so (0x00007fada31f7000)
libstdc++.so.6 => /usr/lib64/libstdc++.so.6 (0x00007fada2ee5000)
libm.so.6 => /lib64/libm.so.6 (0x00007fada2c8e000)
libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007fada2a78000)
libc.so.6 => /lib64/libc.so.6 (0x00007fada270a000)
/lib64/ld-linux-x86-64.so.2 (0x00007fada3669000)
结论
我的印象是,这应该可行,但我遗漏了一个细节。我的第一个猜测是 memcpy@GLIBC_2.14
与 memcpy@@GLIBC_2.14
不匹配。
PS
我很难找到线索,因为 bing 或 google 似乎不支持搜索“@”和“@@”。虽然 readelf
有很多文档,但每个文档只涵盖一小部分。通常会遗漏我要找的东西。因此,如果解决方案在某处有详细记录,请耐心等待。
glibc 动态链接器目前不支持此功能。原来的 GNU symbol versioning specification 需要多次一致性检查,这些检查无法关闭并阻止通过预加载向现有库添加全新的符号版本。
首先,您应该简单地插入一个未版本化的符号。所有符号版本都将绑定到它。由于您可以安排没有可观察到的行为差异(只需调用 memmove
),因此您不需要在此处进行细粒度控制。符号 memcpy@GLIBC_2.14
不会插入任何内容,因此原始规范要求动态链接器检查符号定义的 soname 是否与符号引用中包含的 soname 匹配,并且在预加载时不会匹配。
但这不会解决您的问题,因为另一个一致性检查:由符号版本参考中的 soname 标识的库必须使用此符号版本来定义 any象征。简而言之,符号版本本身的绑定并不懒惰。即使从未使用过符号版本,它也会失败,这可能是由于预加载,也可能是因为实际的符号引用是惰性的且从未绑定。
我们可能应该删除第一个检查(我们 运行 由于它导致 glibc 本身出现问题),并添加一个旋钮来关闭第二个检查。但是您必须将该更改反向移植到您的 2.11 派生的 glibc 中,此时,您可以简单地重建它并为 memcpy
添加一个带有版本符号 memcpy@GLIBC_2.14
的别名。对不起。