为什么针对已安装的 glibc 编译的程序不能正常 运行?
Why does the program which is compiled against the installed glibc not run normally?
提前致谢。
我的开发环境:
$ cat /proc/version
Linux version 5.4.0-66-generic (buildd@lgw01-amd64-016) (gcc version 7.5.0 (Ubuntu 7.5.0-3ubuntu1~18.04)) #74~18.04.2-Ubuntu SMP Fri Feb 5 11:17:31 UTC 2021
$ ld --version
GNU ld (GNU Binutils for Ubuntu) 2.30
Copyright (C) 2018 Free Software Foundation, Inc.
$ getconf GNU_LIBC_VERSION
glibc 2.27
$ #my glibc source version is 2.32.9000-development
$ cat ./version.h
/* This file just defines the current version number of libc. */
#define RELEASE "development"
#define VERSION "2.32.9000"
由于某些原因,我需要修改和测试glibc。我按照这个网站(https://sourceware.org/glibc/wiki/Testing/Builds#Compile_against_glibc_in_an_installed_location)的步骤修改glibc并编写测试程序。
- 编译 glibc。(配置和制作)
- 安装glibc。(安装到一个目录)
- ...其他步骤在上面的网站中。
我成功修改了一些pthread函数并通过了测试(我写的测试程序可以针对install glibc编译并且运行成功)。 ldd 程序。
$ ldd ./exec/1-1.out
linux-vdso.so.1 (0x00007ffcbf367000)
libpthread.so.0 => /home/cjl-target/gnu/install/lib64/libpthread.so.0 (0x00007fcadcea9000)
libc.so.6 => /home/cjl-target/gnu/install/lib64/libc.so.6 (0x00007fcadcaed000)
/home/cjl-target/gnu/install/lib64/ld-linux-x86-64.so.2 => /lib64/ld-linux-x86-64.so.2 (0x00007fcadd2ca000)
如上图,程序依赖的共享库都指向glibc的安装路径
但是当我编译消息队列的测试程序(测试mq_unlink)和运行它时,失败如下:
./exec/1-1.out: symbol lookup error: /lib/x86_64-linux-gnu/libpthread.so.0: undefined symbol: __libc_vfork, version GLIBC_PRIVATE
检查程序依赖的库:
$ ldd ./exec/1-1.out
linux-vdso.so.1 (0x00007ffce3f72000)
librt.so.1 => /home/cjl-target/gnu/install/lib64/librt.so.1 (0x00007f0a389a2000)
libc.so.6 => /home/cjl-target/gnu/install/lib64/libc.so.6 (0x00007f0a385e6000)
libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f0a383c7000)
/home/cjl-target/gnu/install/lib64/ld-linux-x86-64.so.2 => /lib64/ld-linux-x86-64.so.2 (0x00007f0a38dac000)
如上图,共享库libpthread.so.0指向系统库。为什么?
我的编译脚本是(来自上面的网站):
# dobuild.sh
SYSROOT=/home/xxx/xxx/xxx #the glibc's installation path
(set -x; \
gcc \
-L${SYSROOT}/usr/lib64 \
-I${SYSROOT}/usr/include \
--sysroot=${SYSROOT} \
-Wl,-rpath=${SYSROOT}/lib64 \
-Wl,--dynamic-linker=${SYSROOT}/lib64/ld-linux-x86-64.so.2 \
-Wall $*
)
当我编译pthread的测试程序时:./dobuild 1-1.c -pthread -Wall
当我编译mq的测试程序时:./dobuild 1-1.c -lrt -Wall
另外,在mq_unlink的测试程序中调用pthread_create,编译./dobuild 1-1.c -lrt -pthread
时,ldd结果显示所有依赖库都指向安装的glibc。
我已经尝试了多种变体,但其中 none 似乎有效。有什么想法吗?
首先,您应该停止使用 ldd
-- 在主机上存在多个 GLIBC 的情况下,ldd
更有可能误导而不是说明。
如果您想查看真正加载了哪些库,请执行以下操作:
LD_TRACE_LOADED_OBJECTS=1 ./exec/1-1.out
其次,您几乎不应该在 shell 脚本中使用 $*
。请改用 "$@"
(注意:引号很重要)。看到这个 answer.
第三,您观察到的行为很容易解释。要理解它,你需要知道 DT_RPATH
和 DT_RUNPATH
之间的区别,描述 here.
您可以验证您的二进制文件当前是否正在使用 RUNPATH
,如下所示:
readelf -d 1-1.out | grep 'R.*PATH'
并且您可以通过将 -Wl,--disable-new-dtags
添加到 link 命令来验证一切是否按预期开始工作(这将导致二进制文件改为使用 RPATH
)。
总结一下:
RUNPATH
影响二进制文件本身的搜索,但 不影响 二进制文件所依赖的任何库。
RPATH
影响二进制 和 它依赖的所有库的搜索路径。
- with
RUNPATH
,预期的 libpthread.so.0
被发现 只有 当二进制文件直接依赖于它时,而不是当依赖于 libpthread
是间接(通过librt
)。
- with
RPATH
,不管依赖是直接的还是间接的,预期的 libpthread.so.0
都会被找到。
更新:
If I want to use DT_RUNPATH, how to set the library runpath for librt?
您需要 link librt.so
和 -rpath=${SYSROOT}/lib64
。
您可以编辑 rt/Makefile
,或构建:
make LDFLAGS-rt.so='-Wl,--enable-new-dtags,-z,nodelete,-rpath=${SYSROOT}/lib64'
您需要对可能对 GLIBC 的其他部分带来传递依赖的任何其他库执行相同的操作。我不知道执行此操作的一般方法,但设置 LDFLAGS-lib.so='-Wl,-rpath=${SYSROOT}/lib64'
并重建一切可能会成功。
提前致谢。
我的开发环境:
$ cat /proc/version
Linux version 5.4.0-66-generic (buildd@lgw01-amd64-016) (gcc version 7.5.0 (Ubuntu 7.5.0-3ubuntu1~18.04)) #74~18.04.2-Ubuntu SMP Fri Feb 5 11:17:31 UTC 2021
$ ld --version
GNU ld (GNU Binutils for Ubuntu) 2.30
Copyright (C) 2018 Free Software Foundation, Inc.
$ getconf GNU_LIBC_VERSION
glibc 2.27
$ #my glibc source version is 2.32.9000-development
$ cat ./version.h
/* This file just defines the current version number of libc. */
#define RELEASE "development"
#define VERSION "2.32.9000"
由于某些原因,我需要修改和测试glibc。我按照这个网站(https://sourceware.org/glibc/wiki/Testing/Builds#Compile_against_glibc_in_an_installed_location)的步骤修改glibc并编写测试程序。
- 编译 glibc。(配置和制作)
- 安装glibc。(安装到一个目录)
- ...其他步骤在上面的网站中。
我成功修改了一些pthread函数并通过了测试(我写的测试程序可以针对install glibc编译并且运行成功)。 ldd 程序。
$ ldd ./exec/1-1.out
linux-vdso.so.1 (0x00007ffcbf367000)
libpthread.so.0 => /home/cjl-target/gnu/install/lib64/libpthread.so.0 (0x00007fcadcea9000)
libc.so.6 => /home/cjl-target/gnu/install/lib64/libc.so.6 (0x00007fcadcaed000)
/home/cjl-target/gnu/install/lib64/ld-linux-x86-64.so.2 => /lib64/ld-linux-x86-64.so.2 (0x00007fcadd2ca000)
如上图,程序依赖的共享库都指向glibc的安装路径
但是当我编译消息队列的测试程序(测试mq_unlink)和运行它时,失败如下:
./exec/1-1.out: symbol lookup error: /lib/x86_64-linux-gnu/libpthread.so.0: undefined symbol: __libc_vfork, version GLIBC_PRIVATE
检查程序依赖的库:
$ ldd ./exec/1-1.out
linux-vdso.so.1 (0x00007ffce3f72000)
librt.so.1 => /home/cjl-target/gnu/install/lib64/librt.so.1 (0x00007f0a389a2000)
libc.so.6 => /home/cjl-target/gnu/install/lib64/libc.so.6 (0x00007f0a385e6000)
libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f0a383c7000)
/home/cjl-target/gnu/install/lib64/ld-linux-x86-64.so.2 => /lib64/ld-linux-x86-64.so.2 (0x00007f0a38dac000)
如上图,共享库libpthread.so.0指向系统库。为什么?
我的编译脚本是(来自上面的网站):
# dobuild.sh
SYSROOT=/home/xxx/xxx/xxx #the glibc's installation path
(set -x; \
gcc \
-L${SYSROOT}/usr/lib64 \
-I${SYSROOT}/usr/include \
--sysroot=${SYSROOT} \
-Wl,-rpath=${SYSROOT}/lib64 \
-Wl,--dynamic-linker=${SYSROOT}/lib64/ld-linux-x86-64.so.2 \
-Wall $*
)
当我编译pthread的测试程序时:./dobuild 1-1.c -pthread -Wall
当我编译mq的测试程序时:./dobuild 1-1.c -lrt -Wall
另外,在mq_unlink的测试程序中调用pthread_create,编译./dobuild 1-1.c -lrt -pthread
时,ldd结果显示所有依赖库都指向安装的glibc。
我已经尝试了多种变体,但其中 none 似乎有效。有什么想法吗?
首先,您应该停止使用 ldd
-- 在主机上存在多个 GLIBC 的情况下,ldd
更有可能误导而不是说明。
如果您想查看真正加载了哪些库,请执行以下操作:
LD_TRACE_LOADED_OBJECTS=1 ./exec/1-1.out
其次,您几乎不应该在 shell 脚本中使用 $*
。请改用 "$@"
(注意:引号很重要)。看到这个 answer.
第三,您观察到的行为很容易解释。要理解它,你需要知道 DT_RPATH
和 DT_RUNPATH
之间的区别,描述 here.
您可以验证您的二进制文件当前是否正在使用 RUNPATH
,如下所示:
readelf -d 1-1.out | grep 'R.*PATH'
并且您可以通过将 -Wl,--disable-new-dtags
添加到 link 命令来验证一切是否按预期开始工作(这将导致二进制文件改为使用 RPATH
)。
总结一下:
RUNPATH
影响二进制文件本身的搜索,但 不影响 二进制文件所依赖的任何库。RPATH
影响二进制 和 它依赖的所有库的搜索路径。- with
RUNPATH
,预期的libpthread.so.0
被发现 只有 当二进制文件直接依赖于它时,而不是当依赖于libpthread
是间接(通过librt
)。 - with
RPATH
,不管依赖是直接的还是间接的,预期的libpthread.so.0
都会被找到。
更新:
If I want to use DT_RUNPATH, how to set the library runpath for librt?
您需要 link librt.so
和 -rpath=${SYSROOT}/lib64
。
您可以编辑 rt/Makefile
,或构建:
make LDFLAGS-rt.so='-Wl,--enable-new-dtags,-z,nodelete,-rpath=${SYSROOT}/lib64'
您需要对可能对 GLIBC 的其他部分带来传递依赖的任何其他库执行相同的操作。我不知道执行此操作的一般方法,但设置 LDFLAGS-lib.so='-Wl,-rpath=${SYSROOT}/lib64'
并重建一切可能会成功。