我正在尝试创建一个共享库,libfunc.so
它依赖于另一个共享库(具体来说libuv.so
,但我认为特定库与问题无关)。即当我运行时ldd libfunc.so
,我想查看从libfunc.so
到 的依赖关系libuv.so
。
这是我想要编译的代码libfunc.so
:
#include <uv.h>
int func() {
uv_timespec64_t now;
uv_clock_gettime(UV_CLOCK_REALTIME, &now);
return 0;
}
...我这样编译:
$ cc --version && cc -fpic -ggdb -Wall -c -o func.o func.c
cc (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0
Copyright (C) 2021 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
$ cc -shared -o libfunc.so func.o -fpic -ggdb -Wall -luv
$
...当我运行时ldd libfunc.so
,我没有看到所需的依赖项libuv.so
:
$ ldd ./libfunc.so
linux-vdso.so.1 (0x00007fff827ae000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f14b291e000)
/lib64/ld-linux-x86-64.so.2 (0x00007f14b2b54000)
$
...即我想看到类似的内容:
linux-vdso.so.1 (0x00007ffcbbdca000)
libuv.so.1 => /lib/x86_64-linux-gnu/libuv.so.1 (0x00007f781b25c000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f781b034000)
/lib64/ld-linux-x86-64.so.2 (0x00007f781b2a2000)
我的问题是:为什么没有libuv.so
出现依赖关系,我需要做什么才能创建该依赖关系?
我的理解可能是初级和不完整的,但我认为创建依赖关系的方式是:1)编写从(共享)库调用函数的代码,2)编译目标代码,3)创建(共享)库与定义丢失符号的库链接时的目标代码。
检查时libfunc.so
,我确实看到了预期的未定义符号libuv
:
$ nm libfunc.so | grep U
0000000000002000 r __GNU_EH_FRAME_HDR
U __stack_chk_fail@GLIBC_2.4
U uv_clock_gettime
$
对于某些上下文,我尝试从较大的项目创建 MVCE。具体来说,该较大的项目创建一个依赖于libuv
. 不过,当我ldd
在较大项目的共享库上运行时,它确实显示了对libuv
(这就是我获得上面“所需”ldd 输出的输出)的依赖关系。
较大的项目太大了,我无法在 Stack Overflow 上发布,但通过检查其make
输出,我相信我的 MCVE 正在使用相同的标志进行编译/链接。例如,来自较大项目的一些编译行和链接行是:
cc -fpic -ggdb -Wall -c -o file1.o file1.c
cc -fpic -ggdb -Wall -c -o file2.o file2.c
cc -shared -o libplugin.so file1.o file2.o -fpic -ggdb -Wall -luv
(还有更多编译的文件包含libplugin.so
,但上面的子集传达了它的要点——所有编译文件的编译标志都是统一的)
更新:如果我在共享库的代码中添加调用uv_close()
,则会显示所需的依赖关系!IE:
#include <uv.h>
int func() {
uv_timespec64_t now;
uv_clock_gettime(UV_CLOCK_REALTIME, &now);
uv_close(NULL, NULL); // <= Adding this line causes the desired dependency to show up in ldd
return 0;
}
$ cc -fpic -ggdb -Wall -c -o func.o func.c
$ cc -shared -o libfunc.so func.o -fpic -ggdb -Wall -luv
$ ldd ./libfunc.so
linux-vdso.so.1 (0x00007ffc1e323000)
libuv.so.1 => /lib/x86_64-linux-gnu/libuv.so.1 (0x00007f412b761000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f412b539000)
/lib64/ld-linux-x86-64.so.2 (0x00007f412b7a1000)
$
有人可以帮助我理解这个观察结果吗?为什么调用uv_clock_gettime()
vs.uv_close()
对于共享库中创建的依赖关系会有这样的行为?
更新:我想探索@WeatherVane 的评论回复:进一步优化。在这里,我的理解可能是初级和不完整的,但我认为如果我用 进行编译-O0
,它将迫使编译器不优化任何内容,因此即使我的共享库仅调用uv_clock_gettime()
. 但现实与这个想法不符:返回func.c
到仅使用 调用uv_clock_gettime()
和编译所有内容-O0
,我仍然认为没有对 的依赖libuv
。IE:
$ cc -fpic -ggdb -O0 -Wall -c -o func.o func.c # Note the -O0
$ cc -shared -O0 -o libfunc.so func.o -fpic -ggdb -luv # Note the -O0
$ ldd ./libfunc.so
linux-vdso.so.1 (0x00007fff8e724000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fee6f03e000)
/lib64/ld-linux-x86-64.so.2 (0x00007fee6f274000)
我想探索 @Barmar 的建议,即通过打印 的值来排除优化的可能性now
,但即使该版本的代码也导致不存在所需的依赖关系。IE:
#include <inttypes.h>
#include <stdio.h>
#include <uv.h>
int func() {
uv_timespec64_t now;
uv_clock_gettime(UV_CLOCK_REALTIME, &now);
printf("%" PRId64 "\n", now.tv_sec);
return 0;
}
$ cc -fpic -ggdb -Wall -c -o func.o func.c
$ cc -shared -o libfunc.so func.o -fpic -ggdb -luv
$ ldd ./libfunc.so
linux-vdso.so.1 (0x00007ffd72bdf000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f0a89964000)
/lib64/ld-linux-x86-64.so.2 (0x00007f0a89b9a000)
探索@EmployedRussian的建议,readelf
输出是:
$ readelf -Ws /usr/local/lib/libuv.so.1 | grep uv_close
337: 0000000000013766 427 FUNC GLOBAL DEFAULT 14 uv_close
735: 0000000000013766 427 FUNC GLOBAL DEFAULT 14 uv_close
$ readelf -Ws /usr/local/lib/libuv.so.1 | grep uv_clock_gettime
341: 00000000000136a0 178 FUNC GLOBAL DEFAULT 14 uv_clock_gettime
1120: 00000000000136a0 178 FUNC GLOBAL DEFAULT 14 uv_clock_gettime
$
我正在根据 @EmployedRussian 的答案更新一些观察结果,因为它们是相关的,但作为评论添加起来很麻烦。
对于仅调用 的共享库版本uv_clock_gettime
,链接器选项的使用-y
显示:
$ cc -shared -Wl,-y,uv_clock_gettime,-y,uv_close -o libfunc.so func.o -fpic -ggdb -luv
/usr/bin/ld: func.o: reference to uv_clock_gettime
/usr/bin/ld: /usr/lib/gcc/x86_64-linux-gnu/11/../../../x86_64-linux-gnu/libuv.so: definition of uv_close
$
uv_clock_gettime
...并且对于引用和 的共享库版本uv_close
,链接器-y
选项显示:
$ cc -shared -Wl,-y,uv_clock_gettime,-y,uv_close -o libfunc.so func.o -fpic -ggdb -luv
/usr/bin/ld: func.o: reference to uv_close
/usr/bin/ld: func.o: reference to uv_clock_gettime
/usr/bin/ld: /usr/lib/gcc/x86_64-linux-gnu/11/../../../x86_64-linux-gnu/libuv.so: definition of uv_close
...这与@EmployedRussian 对引用的符号与找到的符号与. 的解释一致DT_NEEDED
。
此外,对于仅引用的共享库版本uv_clock_gettime
,使用链接器--no-as-needed
标志确实“强制”包含依赖项:
$ cc -shared -Wl,--no-as-needed -o libfunc.so func.o -fpic -ggdb -luv
$ ldd ./libfunc.so
linux-vdso.so.1 (0x00007ffe312cf000)
libuv.so.1 => /lib/x86_64-linux-gnu/libuv.so.1 (0x00007f36fe4f8000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f36fe2d0000)
/lib64/ld-linux-x86-64.so.2 (0x00007f36fe538000)
$
这或许就是答案。我猜
/lib/x86_64-linux-gnu/libuv.so.1
正在链接时使用,并且-Wl,--as-needed
您的 GCC默认配置为通过。如果这两者都是 true,那么当您链接
func.o
不引用时uv_close
,链接器会发现/lib/x86_64-linux-gnu/libuv.so.1
但发现它不满足任何符号,因此不会将其记录为DT_NEEDED
forlibfoo.so
。当您更改
func.o
为also require时uv_close
,链接器会观察到/lib/x86_64-linux-gnu/libuv.so.1
满足该符号所必需的,并将其记录在标签DT_NEEDED
中libfoo.so
。要确认这些猜测,请链接
libfoo.so
到-Wl,-y,uv_clock_gettime,-y,uv_close
flag。这应该显示哪些二进制文件引用以及哪些定义了这两个符号。你也可以用
-Wl,--no-as-needed
-- 链接,这样libuv.so.1
无论它是否满足任何符号都会显示。