我正在try 创建一个共享库libfunc.so,它依赖于另一个共享库(特别是libuv.so,但我认为特定的库与问题无关).也就是说,当我运行ldd libfunc.so时,我希望看到从libfunc.solibuv.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)

问题:why doesn't a dependency on 100 show up, and what do I need to do to create that dependency?

我的理解可能是初级的和不完整的,但我认为创建依赖关系是这样的: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
$

在某些情况下,我正在try 从更大的项目创建一个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,但上面的子集传达了它的要点--对于所有编译的文件,编译标志是统一的)


Update:如果我在共享库的代码中添加一个对uv_close()的调用,所需的依赖关系就会出现!即:

#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()与调用uv_close()的行为方式是这样的?


更新:我想更多地探索@weathervane的 comments RE:优化.再说一次,我的理解可能是初级的和不完整的,但我认为如果我用-O0编译,会迫使编译器不优化任何东西,因此即使我的共享库只调用uv_clock_gettime()也会导致依赖.但现实并不符合这个 idea :将func.c返回到只调用uv_clock_gettime(),并用-O0编译所有东西,我仍然看不到对libuv的依赖.即:

$ 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来排除优化输出的可能性,但即使是该版本的代码也不会产生所需的依赖项.即:

#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)

在探索@Employed俄语的建议时,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
$

我根据@Employed俄语的回答更新了这篇文章,因为它们是相关的,但作为 comments 添加起来很麻烦.

对于仅调用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_gettimeuv_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

...这与@Employed俄语对引用的符号与找到的符号与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)
$

推荐答案

我注意到的另一件事是:我需要grep /usr/local/lib/libuv.so.1而不是/lib/x86_64-linux-gnu/libuv.so.1,因为后者没有uv_clock_gettime符号.

这是probably个答案.我猜是因为

  1. /lib/x86_64-linux-gnu/libuv.so.1在链接时被使用,并且
  2. 您的GCC默认设置为超过-Wl,--as-needed.

如果这两个条件都为真,则当您链接func.odoesn't引用uv_close时,链接器找到/lib/x86_64-linux-gnu/libuv.so.1,但发现它不满足任何符号,因此不会将其记录为libfoo.soDT_NEEDED.

当您将func.o更改为同样需要uv_close时,链接器观察到/lib/x86_64-linux-gnu/libuv.so.1是满足该符号所必需的,并将其记录在libfoo.soDT_NEEDED标记中.


要确认这些猜测,请使用-Wl,-y,uv_clock_gettime,-y,uv_close标志链接libfoo.so.这应该会显示哪些二进制文件引用了这两个符号,哪些定义了这两个符号.

你也可以链接到-Wl,--no-as-needed --在这种情况下,libuv.so.1将显示出来,不管它是否满足任何符号.

C++相关问答推荐

CC crate 示例不会与C函数链接

为什么静态说明符为内联函数生成外部定义?

Apple Libm的罪恶功能

使用双指针动态分配和初始化2D数组

Flose()在Docker容器中抛出段错误

为什么memcpy进入缓冲区和指向缓冲区的指针工作相同?

从uint8_t*转换为char*可接受

什么是.c.h文件?

如何在C中只对字符串(包含数字、单词等)中的数字进行重复操作?

链接器脚本和C程序使用相同的头文件,这可能吗?

浮动目标文件,数据段

强制GCC始终加载常量(即只读),即使启用了优化

我正在使用c学习数据 struct ,在学习堆栈时,我试图将中缀转换为后缀,并编写了这段代码.代码未给出输出

为什么这个代码的最后一次迭代不能正常工作?

将char*数组深度复制到 struct 中?

C程序printf在getchar while循环后不工作

当 n 是我们从用户那里获得的整数时,创建 n 个 struct 参数

C Makefile - 如何避免重复提及文件名

C 语言中霍尔分区的快速排序算法

Zig 中 C 的system函数的惯用替代方案