我在使用包含弱符号和 --as-needed 链接器标志的库时遇到问题。
不你不是。
找出你libjack.so
的位置,例如
$ locate libjack
/usr/lib/x86_64-linux-gnu/libjack.so
/usr/lib/x86_64-linux-gnu/libjack.so.0
/usr/lib/x86_64-linux-gnu/libjack.so.0.1.0
...
然后用于nm
检查 JACK API 的符号类型libjack.so
:
$ nm -C -D /usr/lib/x86_64-linux-gnu/libjack.so | grep jack_
000000000000e7e0 T jack_acquire_real_time_scheduling
000000000000d530 T jack_activate
000000000002ccf0 T jack_client_close
000000000000e820 T jack_client_create_thread
....
....
000000000000f340 T jack_uuid_empty
000000000000f320 T jack_uuid_parse
000000000000f2e0 T jack_uuid_to_index
000000000000f330 T jack_uuid_unparse
您会发现它们都是类型T
(= 文本部分中的普通全局符号:)man nm
。库中有
一些弱符号:
$ nm -C -D /usr/lib/x86_64-linux-gnu/libjack.so | egrep ' (w|W) '
w __cxa_finalize
w __gmon_start__
w _ITM_deregisterTMCloneTable
w _ITM_registerTMCloneTable
w _Jv_RegisterClasses
0000000000025410 W std::ctype<char>::do_widen(char) const
0000000000014c10 W void std::vector<unsigned short, std::allocator<unsigned short> >::_M_emplace_back_aux<unsigned short const&>(unsigned short const&)
0000000000014b10 W std::pair<std::_Rb_tree_iterator<unsigned short>, bool> std::_Rb_tree<unsigned short, unsigned short, std::_Identity<unsigned short>, std::less<unsigned short>, std::allocator<unsigned short> >::_M_insert_unique<unsigned short>(unsigned short&&)
0000000000014ad0 W std::_Rb_tree<unsigned short, unsigned short, std::_Identity<unsigned short>, std::less<unsigned short>, std::allocator<unsigned short> >::_M_erase(std::_Rb_tree_node<unsigned short>*)
但它们都不在 JACK API 中。除了重建你的东西之外,你无能为力libjack.so
。描述问题的正确方法是:
我在将带有--as-needed
链接器标志的库链接到我决定削弱对该库的所有引用的程序时遇到问题
JACK API 的定义符号引用libjack.so
都很强大。您已经编写了一个程序,指示编译器在您的目标代码中发出对 JACK API 的弱未定义引用的符号,并且您发现,通过按需链接,这些弱引用无法强制链接libjack.so
提供它们的缺少定义。
看来问题是:
jack 将所有符号声明为弱符号(如果我包含 )。
当使用 --as-needed 链接时,链接器会排除任何不引用至少一个非弱符号的库。
某些操作系统(例如 Ubuntu-16.04LTS)默认启用了 --as-needed。
最后两点是对的。默认情况下根据需要链接共享库的发行版与不回到 Debian Wheezy, 2013 的发行版之间的分裂
,后者转到了as-needed。从那时起,源自 Debian 的发行版家族也纷纷效仿,而 RedHat/Fedora 家族则一直坚持现状。
第一点很混乱。libjack.so
,正如我们已经注意到的,导出一个强定义的 JACK API,你不能通过编写和编译新代码来改变它。如果您包含<jack/weakjack.h>
在您的源文件之一中,那么您将在您的代码中声明所有 JACK API 符号为弱,并且编译器将为您提供一个仅包含对 JACK API 的弱引用的目标文件。<jack/weakjack.h>
只定义具有这种效果的宏。
如果一个像旧的和主要的 linux 库这样的旧的和主要的 linux 库libjack
无法适应所需的分裂,那将是令人惊讶的。我怀疑您忽略了一些关于以下内容的小字jack/weakjack.h
:
详细说明
开发人员面临的一个挑战是利用 [JACK] 新版本中引入的新功能,同时仍支持旧版本的系统。通常,如果应用程序使用库/API 中的新功能,则无法在不支持该功能的早期版本的库/API 上运行。当尝试使用该功能时,此类应用程序将无法启动或崩溃。这个问题可以使用弱链接符号来解决。
...
一个具体的例子会有所帮助。假设有人使用我们称为“Jill”的 JACK 客户端版本。Jill 与包含 API 的更新部分(例如 jack_set_latency_callback())的 JACK 版本相关联,并且如果可用,希望使用它。
当 Jill 在具有适当“新”版本 JACK 的系统上运行时,此功能将完全正常可用。但是,如果 Jill 在具有旧版本 JACK 的系统上运行,则该功能不可用。
使用正常的符号链接,每当有人尝试使用“旧”版本的 JACK 运行 Jill 时,都会产生启动错误。但是,在 0.116.2 版本之后添加到 JACK 的函数都被声明为具有“弱”链接,这意味着它们的缺失不会在程序启动期间导致错误。相反,Jill 可以测试符号 jack_set_latency_callback 是否为空。如果为空,说明本机安装的JACK太旧,不支持该功能。如果它不为 null,那么 Jill 可以像使用 API 中的任何其他函数一样使用它。例如:
if (jack_set_latency_callback) {
jack_set_latency_callback (jill_client, jill_latency_callback, arg);
}
但是,有些客户可能希望对
0.116.2 之前的 JACK API 部分使用这种方法。例如,他们可能想查看
API 的非常古老的基本部分(如 jack_client_open())是否在运行时存在。
此类客户端应在任何其他 JACK 标头之前包含 <jack/weakjack.h>。
这将使整个 JACK API 受到弱链接,因此
可以在运行时检查任何和所有函数是否存在。重要的是要
了解很少有客户需要这样做 - 如果您使用此功能,您
应该有明确的理由这样做。
[重点补充]
这清楚地表明,像您这样的程序,jack/weakjack.h
为了削弱其对整个 JACK API 的引用而采取了包含特殊步骤的特殊步骤,只有在它在引用它之前测试每个 JACK API 符号的定义性时才能预期成功运行。并处理未定义的情况。你的程序不符合。这个可以:
myjack1.c
#include <jack/weakjack.h>
#include <jack/jack.h>
#include <stdio.h>
int main() {
if (jack_client_open) {
jack_client_open("foobar", JackNoStartServer, 0, 0);
} else {
puts("`jack_client_open` is not available");
}
return 0;
}
做这个:
myjack2.c
#include <jack/weakjack.h>
#include <jack/jack.h>
#include <dlfcn.h>
#include <stdlib.h>
#include <stdio.h>
int main() {
jack_client_t * (*jack_client_open_fp)
(const char *, jack_options_t,jack_status_t *,...) = jack_client_open;
if (!jack_client_open_fp) {
void * dsoh = dlopen("libjack.so",RTLD_LAZY);
if (!dsoh) {
fputs("`libjack` is not available\n",stderr);
exit(EXIT_FAILURE);
}
*(void**)(&jack_client_open_fp) = dlsym(dsoh,"jack_client_open");
if (!jack_client_open_fp) {
fputs("`jack_client_open` is not available\n",stderr);
exit(EXIT_FAILURE);
}
}
jack_client_open_fp("foobar", JackNoStartServer, 0, 0);
exit(EXIT_SUCCESS);
}
它勾勒了可发现 API 的常用方法——适用于旨在在可能根本不提供的系统上安装和运行的程序libjack
。因此,您无需参考以下内容即可构建它libjack
:
gcc -o myjack2 myjack2.c -ldl
在 Ubuntu 17.04 上——它确实提供libjack
了——它可能会像这样运行:
$ ./myjack2
Cannot connect to server socket err = No such file or directory
Cannot connect to server request channel
jack server is not running or cannot be started
JackShmReadWritePtr::~JackShmReadWritePtr - Init not done for 4294967295, skipping unlock
JackShmReadWritePtr::~JackShmReadWritePtr - Init not done for 4294967295, skipping unlock
因此,图书馆的 T&C 在按需链接方面处于良好状态。这似乎让您独立地不满意按需链接的工作方式,而不是以不同的方式让您削弱对 JACK API 的所有引用并且仍然需要libjack
您的对其 API 符号的弱引用:-
我不明白为什么弱依赖被认为根本没有依赖。对我来说,弱依赖是启用可选功能。如果可能,我确实希望启用这些功能,并且是否可能的决定应该是运行时决定。对于当前的行为,它变成了编译时的决定。
您认为弱符号引用会导致对定义符号的库的弱链接依赖的观点对于 GNU 链接器没有立足点。如果程序的链接需要库提供的符号定义,则程序依赖于库;否则它不依赖于那个库:没有弱和强的依赖程度。(Darwin Mach-O 链接器确实支持同源区分)
有弱符号,与默认和通常的类型相反,即强。{weak|strong} 符号是{weakly|strongly} 引用符号的简写,因为同一个符号可能在多个链接器输入文件中被引用,有时或总是弱引用,有时或总是强引用。
强符号在链接中必须有一个定义引用。
弱符号是这样的:
从第一部分可以看出,对符号的未定义弱引用根本不会产生链接依赖。不需要定义,
不需要定义的事实是程序员(例如#include <jack/weak_jack.h>
)或编译器决定的结果。期望链接器(如果被指示仅链接需要的共享库)然后应该链接库以提供您或编译器已告诉它不需要定义的符号定义是不合理的。
如果链接器在您的情况下表现得像这样,那将构成一个链接时间决定冻结和启用一个 API,通过包含jack/weak_jack.h
,您表示您希望完全保留用于运行时发现。
将您的问题程序与之链接-no-as-needed
是成功消除程序中错误的一种方式。错误在于,通过将jack/weak_jack.h
您自己承诺到整个 API 的运行时发现,但不履行该承诺,而是将 API 的可用性视为理所当然。因此,具有按需链接的段错误。链接 with-no-as-needed
只是取消了 include 的效果jack/weak_jack.h
。包含它表示您的程序
不需要任何 API 定义:-no-as-needed
表示,无论它们是什么,无论如何您都会得到它们。
鉴于所有 JACK API 发布版本 0.116.2 的定义很弱而没有诉诸jack/weak_jack.h
,我认为除非您确实正在计划一个可以在libjack
缺少的主机。如果您
计划这样做,那么无论链接约定如何,您都无法在运行时发现您使用的所有 JACK API,因为
libjack
无论如何您都无法链接。
如果不是,那么只需链接libjack
,如果您只是调用jack_client_open
,您的程序在任何主机上都会动态链接所有 API 定义,无论它们在该主机上是什么,因为您对jack_client_open
(在没有 的情况下<jack/weak_jack.h>
)的引用将libjack
需要,无论这对链接或不链接的链接器很重要。如果您希望跨 API 版本兼容,那么您需要实现运行时检测
,如使用该属性记录
的任何 API 的记录JACK_WEAK_EXPORT
- 而不是JACK_OPTIONAL_WEAK_EXPORT, or JACK_OPTIONAL_WEAK_DEPRECATED_EXPORT
:后者表示只能通过<jack/weak_jack.h>
.