我经常使用使用 Unix/Linux C 库的第三方 C 代码片段,例如#include <glib.h>
.#include <net/if.h>
他们要求我知道具体的库文件名或 pkg-config 名称,例如-lglib-2.0
, -lbluetooth
, pkg-config --libs dbus-1
.请注意,其中很多与具有“2.0”、“1”的头文件不同,因此您无法猜测它。
我到底如何才能找到 API/库所拥有的库文件的确切名称?使用我上面的例子;我如何找到该#include <glib.h>
库文件的名称-lglib-2.0
?令人沮丧的是,Glib/dbus/all 的在线参考文献不仅仅说“库文件的名称是 glib-2.0”。
我可以使用任何终端命令来找出这个问题吗?我可以使用一些实用程序来找出这个问题吗?在线 API/库参考对于找出这一点来说确实很糟糕。
如果我想找到它hci.h
的位置,我可以轻松地locate hci.h
找到它的位置。有没有类似终端命令的东西来查找库名称?
答案1
没有通用方法可以知道使用特定函数需要哪个库。您需要查看该库的文档。编写良好的教程或 API 参考应该告诉您,这就是它的工作。
您至少可以了解什么是图书馆包裹是必需的:它与包含头文件的库包相同。如何确定哪个包包含头文件取决于发行版,例如dpkg -S /usr/include/glib-2.0/glib.h
在 Debian/Ubuntu/Mint/...、rpm -qf /usr/include/glib-2.0/glib.h
RHEL/CentOS/Fedora/...等。知道库包后,列出其内容以查找哪些.so
文件它包含,例如
dpkg -L libglib2.0-dev | grep '\.so$'
rpm -qlf /usr/include/glib-2.0/glib.h | grep '\.so$'
对于 Glib,有多个.so
文件,并且没有自动方法来判断特定函数需要哪些文件(可能有多个文件)。
如果库使用pkg-config
,您应该使用它而不是硬编码标头和库路径。然而,并非所有图书馆都使用它。您通常可以看出,因为您的发行版的库包应该依赖于它的pkg-config
包(例如dpkg -s libglib2.0-dev |grep '^Depends:.*pkg-config'
)。
如果文档真的很糟糕,请尝试列出库定义的符号:
nm -D /usr/lib/x86_64-linux-gnu/libgio-2.0.so |awk '$2=="T" {print $3}'
这不是一种万无一失的方法,因为它不会告诉您是否还需要其他库。
答案2
这些#include
指令由 C 预处理器扩展。搜索路径由编译器控制(例如-I<dir>
标志)。它们在生成的目标文件中提供符号。
然后,链接器使用约定将这些符号映射到存档/目标文件:给定类似 的标志-l<namespec>
,链接器将在其库路径中的目录中搜索lib<namespec>.a
或lib<namespec>.so
。链接器将这些文件名包含在生成的二进制文件中,动态链接器在运行时使用这些文件名将符号解析为其实际实现。
您可以使用 找到编译器链接器的搜索目录ld --verbose | grep SEARCH_DIR
。例如,这大致模拟链接器如何将库名称映射到文件.so
:
$ libname=glib-2.0; ld --verbose | grep SEARCH_DIR | tr -s ' ;' '\n' | sed 's/SEARCH_DIR("=\?\(.*\)")/\1/g' | xargs -I{} find {} -maxdepth 1 2>/dev/null | grep lib${libname}.so
/usr/lib/libglib-2.0.so.0.7000.0
/usr/lib/libglib-2.0.so
/usr/lib/libglib-2.0.so.0
(这些都是同一文件的符号链接。)
您可以在包管理器中找到提供该文件的包,例如yum
:
$ yum whatprovides /usr/lib64/libglib-2.0.so.0
glib2-2.68.1-1.fc34.x86_64 : A library of handy utility functions
Repo : fedora
Matched from:
Filename : /usr/lib64/libglib-2.0.so.0
glib2-2.68.4-1.fc34.x86_64 : A library of handy utility functions
Repo : @System
Matched from:
Filename : /usr/lib64/libglib-2.0.so.0
glib2-2.68.4-1.fc34.x86_64 : A library of handy utility functions
Repo : updates
Matched from:
Filename : /usr/lib64/libglib-2.0.so.0
您可以使用以下命令查看其中的符号nm
:
$ nm -D --defined-only /usr/lib/libglib-2.0.so.0
0000000000070000 T g_access
000000000001d600 T g_allocator_free
000000000001d5f0 T g_allocator_new
0000000000022810 T g_array_append_vals
000000000001e3a0 T g_array_binary_search
...
链接器约定记录在 中man ld
。
最后回答您的问题:这些机制旨在将头文件与实际实现分离,因此没有通用的方法可以根据设计将头文件映射到对象/归档文件。最终你必须找到.so
实现你的符号的文件#include
。正如另一个答案所说,这是临时的。
不过,包管理器对它有很大帮助,因为通常是提供头文件的包,<package>-devel
并且该包<package>
提供共享对象。例如,yum
您可以使用它来查找开发包:
$ yum provides '*/glib.h'
glib-devel-1:1.2.10-62.fc34.i686 : Libraries and header files for glib development
Repo : fedora
Matched from:
Filename : /usr/include/glib-1.2/glib.h
glib-devel-1:1.2.10-62.fc34.x86_64 : Libraries and header files for glib development
Repo : fedora
Matched from:
Filename : /usr/include/glib-1.2/glib.h
glib2-devel-2.68.1-1.fc34.i686 : A library of handy utility functions
Repo : fedora
Matched from:
Filename : /usr/include/glib-2.0/glib.h
...
可以看到glib2-devel
包中提供了头文件。因此,glib2
包提供了该.so
文件,并且从该文件名,您可以知道该标志要使用什么-l
。
$ repoquery -l glib2 | grep '.so'
/usr/lib/libgio-2.0.so.0
/usr/lib/libgio-2.0.so.0.6800.4
/usr/lib/libglib-2.0.so.0
/usr/lib/libglib-2.0.so.0.6800.4
...
所以你可以看到,鉴于glib.h
,你至少应该使用-lglib-2.0
.