拱

运行示例 C 代码是一项痛苦的工作,除非它附带 makefile。

我经常发现自己有一个 C 文件,其中包含的代码据说可以做一些非常酷的事情,但是第一次基本的编译尝试 ( gcc main.c) 失败了——

main.c:(.text+0x1f): undefined reference to `XListInputDevices'
clang-3.7: error: linker command failed with exit code 1 (use -v to see invocation)

——或类似的。

我知道这意味着我缺少正确的链接器标志,例如-lX11,-lXext-lpthread

但哪些呢?


我目前处理这个问题的方法是查找包含函数的库头,使用 Github 的搜索来查找导入相同标头的其他程序,打开其 makefile,找到链接器标志,将它们复制到我的编译命令中,然后继续删除标志,直到找到仍然可以编译的最小集。

这是低效、无聊的,让我觉得一定有更好的方法。

答案1

问题是如何通过检查源文件来确定要使用的链接器标志。下面的示例适用于 Debian。头文件是这里需要注意的相关项目。

因此,假设有一个包含头文件的 C 源文件

#include <X11/extensions/XInput.h>.

我们可以使用 进行搜索XInput.h,例如apt-file.如果您知道这个头文件包含在已安装的包中,dpkg -S或者dlocate也将起作用。例如

apt-file search XInput.h
libxi-dev: /usr/include/X11/extensions/XInput.h

这告诉您该头文件属于 libxi 的开发包(对于 C 库,开发包(通常采用libname-dev或的形式libname-devel)包含头文件),因此您应该使用-lxi链接器标志。

类似的方法应该适用于任何具有包管理系统的发行版。

答案2

问题的核心:头文件与库名称

问题的核心已发布在问题本身下方的评论:

对于 XListInputDevices,适当的包含行是#include <X11/extensions/XInput.h>。我在想我如何根据 -lSomething 标志判断我需要什么

-lXi为了正确回答这个问题,我们需要理解(或者更确切地说libXi.so)和之间的区别XInput.h。这XInput.h是一个头文件-l查找目标文件。根据有关 gcc 使用的 Red Hat 文档,第16.1节,

库使用特殊的文件名约定:名为 foo 的库预计以文件 libfoo.so 或 libfoo.a 的形式存在。 GCC 的链接输入选项会自动理解此约定,但输出选项不会自动理解此约定

标头和库都是必需的,并且两者相关,但不相同(参考):

...如果您#include 标头,编译器不会将您调用的函数的代码插入program.o 中。它只是插入对它们的引用。

如前所述,标头和链接器有些不同,因为标头存储函数和其他内容的声明(或外行术语 - 描述),而库 - 存储实际对象(参考)或这些函数的编译版本。正如这个答案所说:

  • 标题是您可以拨打的电话号码,而...
  • ...图书馆是您可以到达的实际人!

标头和库如何一起发挥作用?它们都是构建应用程序的必要步骤。首先,收集功能和接口的描述(又名编译的)然后你写的代码是链接的到实现实际接口和功能的库。

图书馆本身应该使用包含的标头进行编译(参考)进入图书馆。事实上你可以看一个例子如何创建简单的静态库

所以你的问题的答案如下:标头和库名称之间没有保证关系。(或者至少到目前为止我还没有找到;另请参阅这个参考文献)。而且,找出如何匹配它们就是了解包或源代码中的内容的问题。

事实上你可以这样做来证明这一点:

$ echo '#include<errno.h> int main(){return 0;}' | gcc -S -lc

这表明我们正在针对 libc 进行编译,但作为 libc 一部分的标头名称不同。

证明这一点的另一个例子是#include <math.h> ,它是 的一部分libc,但实际实现是在 中libm.so.6,因此您将看到用 编译的代码gcc -lm

$ apt-file find  /lib/x86_64-linux-gnu/libm.so.6 
libc6: /lib/x86_64-linux-gnu/libm.so.6

查找相关的.so或.a库文件

很多谜团可以通过文档来解决。我们参考一下gcc链接选项的文档(注意,粗体强调的是我的):

-图书馆

-l 库

链接时搜索名为library的库。 (将库作为单独参数的第二种选择仅用于 POSIX 合规性,不推荐。)

-l 选项是由 GCC 直接传递给链接器。有关确切的详细信息,请参阅链接器文档。下面的一般描述适用于 GNU 链接器。

好的,所以它gcc本身实际上并不是在查找库。这是链接器的工作。但让我们继续读下去。

链接器搜索标准目录列表对于图书馆。搜索的目录包括几个标准系统目录以及您使用 -L 指定的任何目录。

因此,有一个标准的目录列表。太好了,范围缩小了。我们如何获取该列表?单程 其中之一是使用,ldconfig -v用户慷慨地解释了这一点特洛托里姆

gcc文档进一步解释:

静态库是目标文件的档案,文件名类似于 liblibrary.a。某些目标还支持共享库,其名称通常类似于 liblibrary.so。如果同时找到静态库和共享库,则链接器会优先链接共享库,除非使用 -static 选项。

在命令中编写此选项的位置会有所不同;链接器按照指定的顺序搜索和处理库和目标文件。因此,“foo.o -lz bar.o”在文件 foo.o 之后但在 bar.o 之前搜索库“z”。如果 bar.o 引用“z”中的函数,则可能不会加载这些函数。

换句话说,您真正指示gcc并随后ld要做的事情是查找文件libxi.so(因此成为共享对象扩展)或libxi.a在标准路径列表中。如果说我们存储了一些东西/optld链接器不知道它,那么将任何东西传递给-l将不起作用。

接受的答案似乎暗示了这一点,凭借与该对应的库的大多数 debian 包名称libwhatever.so名称相对应的库的大多数 debian 包名称。但我无法确认或否认这一点,因为我对 Debian 打包的了解足够多,只是为了满足我的卑微需求,而不是这些微小的细节。我所知道的是apt 可能会触发ld缓存重建

现在,同样重要的是要意识到,ld实际上所有这些目录都在缓存中/etc/ld.so.cache。因此,如果从缓存中删除目录,链接器将不知道库的存在。


当然,如果我们想基于 的例子来测试上述所有理论libxi-devel,我们可以这样做:


    $ dpkg-query -L libxi-dev | grep -i '.so'
    /usr/share/man/man3/XIDefineCursor.3.gz
    /usr/lib/x86_64-linux-gnu/libXi.so
    /usr/share/man/man3/XIUndefineCursor.3.gz

然后检查ldconfig确认是否/usr/lib/x86_64-linux-gnu在搜索目录列表中...它是:

$ ldconfig -p | grep libXi.so
    libXi.so.6 (libc6,x86-64) => /usr/lib/x86_64-linux-gnu/libXi.so.6
    libXi.so.6 (libc6) => /usr/lib/i386-linux-gnu/libXi.so.6
    libXi.so (libc6,x86-64) => /usr/lib/x86_64-linux-gnu/libXi.so

现在这也提出了一个问题,这个.h文件没用吗?不,显然gcc仍然需要它在编译时存在(因为gcc确实检查了包含路径并且因为有些库只能是头文件

$ mv /usr/include/X11/extensions/XInput.h /tmp
$ echo '#include <X11/extensions/XInput.h> void main(){return 0;}' | gcc -lXi -E -
# 1 "<stdin>"
# 1 "<built-in>"
# 1 "<command-line>"
# 31 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 32 "<command-line>" 2
# 1 "<stdin>"
<stdin>:1:36: warning: extra tokens at end of #include directive
<stdin>:1:10: fatal error: X11/extensions/XInput.h: No such file or directory
compilation terminated.

答案3

这是一个社区 wiki,用于收集其他发行版的等效工具法希姆的方法。请随意编辑,但请保持按字母顺序排序以便搜索。

pkgfile从存储库中使用extra,将头文件名作为参数传递。

例子:

$ pkgfile XInput.h
extra/libxi
extra/nx-headers

德班

(和任何基于 Debian 的东西使用dpkg

apt-file search对于头文件名,如所涵盖的

根图

正如 warl0ck 的另一个问题所述,使用pfl包的e-file程序,或者,基于网络的 Portage 文件列表搜索

相关内容