简体   繁体   English

弱连接与“--as-needed”

[英]Weak-linking vs “--as-needed”

I'm having trouble with using a library that contains weak-symbols and the --as-needed linker flag. 我在使用包含弱符号和--as-needed链接器标志的库时遇到问题。

Example

(This uses the Jack library) (这使用了Jack库)

$ cat <<EOF >myjack.c
#include <jack/weakjack.h>
#include <jack/jack.h>
int main() {
  if (jack_client_opent)
     jack_client_open("foobar", JackNoStartServer, 0, 0);
  else return 1;
  return 0;
}
EOF

$ gcc -o myjack myjack.c  -Wl,--no-as-needed -ljack

$ ./myjack && echo "ok" || echo "K.O."
ok

$ ldd myjack | grep jack
    libjack.so.0 => /usr/lib/x86_64-linux-gnu/libjack.so.0 (0x00007f16f615f000)

$ gcc -o myjack myjack.c  -Wl,--as-needed -ljack

$ ./myjack && echo "ok" || echo "K.O."
K.O.

$ ldd myjack | grep jack

$

(The example code was edited to not segfault any more, as the segfault is not my actually problem) (示例代码被编辑为不再是段错误,因为段错误不是我的实际问题)

The problem 问题

It seems that the problem is: 似乎问题是:

  • Jack declares all symbols as weak (if I include <jack/weakjack.h> ). Jack声明所有符号都是弱的 (如果我包含<jack/weakjack.h> )。 this is fine with me; 这对我很好; I do want my symbols to stay weak. 我确实希望我的符号保持弱势。 esp. ESP。 my program is weakly linking against jack on OSX ( -weak_framework Jackmp ), which requires to include <jack/weakjack.h> 我的程序与OSX上的jack( -weak_framework Jackmp )弱连接,需要包含<jack/weakjack.h>

  • When linking with --as-needed , the linker excludes any library, that does not reference at least one non-weak symbol. 当与--as-needed链接时,链接器会排除任何不引用至少一个非弱符号的库。 from the manpage: 从联机帮助页:

--as-needed causes a DT_NEEDED tag to only be emitted for a library that at that point in the link satisfies a non-weak undefined symbol reference from a regular object file --as-needed导致仅为库中发出DT_NEEDED标记,该库在链接中的该点满足来自常规对象文件的非弱未定义符号引用

  • some OSs (eg Ubuntu-16.04LTS) have --as-needed enabled by default. 某些操作系统(例如Ubuntu-16.04LTS)默认启用--as-needed

Now I think that --as-needed is a nice linker feature to get rid of many really unneeded runtime dependencies. 现在我认为--as-needed是一个很好的链接器功能,可以摆脱许多真正不需要的运行时依赖项。

However, I fail to see why a weak dependency is considered as no dependency at all . 但是,我没有看到为什么依赖被认为根本没有依赖 For me, a weak dependency is to enable optional features. 对我来说, 依赖是启用可选功能。 I do want these features to be enabled if possible, and the decision whether this is possible should be a runtime decision. 我确实希望在可能的情况下启用这些功能,并且决定这是否可行应该是运行时决定。 With the current behavior, it becomes a compile-time decision. 使用当前行为,它成为编译时决策。 (If I wanted that, I would simply disable the relevant code via some preprocessor magic). (如果我想要,我会通过一些预处理器魔术禁用相关代码)。

One solution is obviously to just add --no-as-needed to the linker flags. 一个解决方案显然只是添加--no-as-needed到链接器标志。 I don't want this: I do want to get rid of overlinking, if my distribution (or whoever compiles my binary) thinks this is the thing to do. 我不想这样:我确实想要摆脱过度链接,如果我的发行版(或编译我的二进制文件的人)认为这是要做的事情。

So I might turn on as-needed after linking in my known-weak library: 因此as-needed在链接到我的已知弱库之后,我可能as-needed打开:

  gcc -o myjack myjack.c  -Wl,--no-as-needed -ljack -Wl,--as-needed ...

but this feels wrong as well, as then all libraries after my forced-needed library are suddenly forced to --as-needed (which might not be what my distribution or whoever compiles my binary thinks that this is the thing to do). 但这也感觉不对,因为在我强制需要的库之后的所有库都突然被迫--as-needed (这可能不是我的发行版或编译我的二进制文件的人认为这是要做的事情)。 It also seems to be adding a lot of cruft to the build chain, just because some library happens to export weak symbols only. 它似乎也为构建链添加了很多东西,只是因为某些库恰好只输出弱符号。 I do not want to manually track all libraries that do this. 我不想手动跟踪执行此操作的所有库。

I also could of course simply not include <jack/weakjack.h> . 我当然也可以包括<jack/weakjack.h> The reason why it is included is because the application also works on OSX, where I do want to optionally depend on the JACK framework (so I link with -weak_framework Jackmp ), and keep my program runnable in th absence of that framework. 包含它的原因是因为应用程序也适用于OSX,我希望可选地依赖于JACK框架(所以我链接-weak_framework Jackmp ),并且在没有该框架的情况下保持我的程序可运行。

I really don't want to clutter my application code because of the subtle differences between linkers on various platforms. 我真的不想混淆我的应用程序代码,因为各种平台上的链接器之间的细微差别。 This is probably the main issue I'm having with all this: why should I add platform-specific code to my application to cater for different linker specifics - I'd probably be OK with adding feature-specific code, eg not including weakjack.h if the compiler has no equivalent for -weak_library or -weak_framework ; 这可能是我所有这一切的主要问题:为什么应该为我的应用程序添加特定于平台的代码以满足不同的链接器细节 - 我可能可以添加特定于功能的代码,例如不包括weakjack.h如果编译器没有-weak_library-weak_framework等价物; but currently it seems that the closest I can get is something like #ifdef __APPLE__ which makes me shudder in this context). 但目前似乎我能得到的最接近的是#ifdef __APPLE__ ,这让我在这种情况下不寒而栗。

So I'd really love some option to force libraries that only have weak symbols to be dylinked nevertheless. 所以我真的很喜欢一些选项来强制只有弱符号的库才能被甩掉。

Is there such a thing? 有这样的事吗?

I'm having trouble with using a library that contains weak-symbols and the --as-needed linker flag. 我在使用包含弱符号和--as-needed链接器标志的库时遇到问题。

No, you're not. 不你不是。

Find out where your libjack.so is, eg 找出你的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
...

Then use nm to examine the symbol types of the JACK API in libjack.so : 然后使用nm来检查libjack.so JACK API的符号类型:

$ 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

You'll find they are all type T ( = ordinary global symbol in text section: man nm ). 你会发现它们都是T型(=文本部分中的普通全局符号: man nm )。 There are a few weak symbols in the library: 有一些弱符号:

$ 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>*)

But none of them are in the JACK API. 但它们都不在JACK API中。 Nothing you can do short of rebuilding your libjack.so is going to change that. 没有什么可以做的,没有重建你的libjack.so将改变这一点。 The right way to characterise your problem is: 表征问题的正确方法是:

I'm having trouble linking a library with the --as-needed linker flag to a program in which I decided to weaken all my references to that library 我无法将库与--as-needed链接器标志链接到一个程序,在该程序中我决定削弱对该库的所有引用

The defining symbol references of the JACK API in libjack.so are all strong. libjack.so中JACK API的定义符号引用都很强大。 You have written a program directing the compiler to emit, in your object code, symbols that are weak undefined references to the JACK API, and you are finding that, with as-needed linkage, these weak references fail to compel linkage of libjack.so to provide their missing definitions. 您编写了一个程序,指示编译器在您的目标代码中发出对JACK API的弱未定义引用的符号,并且您发现, 根据需要的链接,这些弱引用无法强制libjack.so提供他们缺少的定义。

it seems that the problem is: 似乎问题是:

jack declares all symbols as weak (if I include ). jack将所有符号声明为弱(如果我包括)。

when linking with --as-needed, the linker excludes any library, that does not reference at least one non-weak symbol. 当与--as-needed链接时,链接器会排除任何不引用至少一个非弱符号的库。

some OSs (eg Ubuntu-16.04LTS) have --as-needed enabled by default. 某些操作系统(例如Ubuntu-16.04LTS)默认启用--as-needed。

The last two points are correct. 最后两点是正确的。 The schism between distros that link shared libraries as-needed by default and distros that don't goes back to Debian Wheezy, 2013, which went over to as-needed . 根据默认需要链接共享库的发行版和不依赖于Debian Wheezy的发行版之间的分歧,2013年,它根据需要进行了修改 Since then, the Debian-derived distro-clan has followed suit while the RedHat/Fedora clan has stuck with the status quo ante . 从那以后,Debian派生的发行家族也纷纷效仿,而RedHat / Fedora部落则坚持原状

The first point is confused. 第一点很困惑。 libjack.so , as we've noted, exports a strongly defined JACK API that you cannot alter by writing and compiling new code. 正如我们所指出的, libjack.so导出一个强定义的JACK API,你不能通过编写和编译新代码来改变它。 If you include <jack/weakjack.h> in one of your source files, then you are declaring all JACK API symbols weak, in your code, and the compiler will give you an object file that contains only weak references to the JACK API. 如果在一个源文件中包含<jack/weakjack.h> ,那么在代码中声明所有JACK API符号都很弱,并且编译器将为您提供一个仅包含对JACK API的弱引用的目标文件。 <jack/weakjack.h> just defines macros that have that effect. <jack/weakjack.h>只定义具有该效果的宏。

It would be surprising if an old and major linux library like libjack has botched its adaptation to the as-needed schism. 如果像libjack这样的旧的和主要的linux库已经适应了所需的分裂,那将是令人惊讶的。 I suspect you've overlooked some of the small print about jack/weakjack.h : 我怀疑你忽略了一些关于jack/weakjack.h

Detailed Description 详细说明

One challenge faced by developers is that of taking advantage of new features introduced in new versions of [ JACK ] while still supporting older versions of the system. 开发人员面临的一个挑战是利用新版本[JACK]中引入的新功能,同时仍然支持旧版本的系统。 Normally, if an application uses a new feature in a library/API, it is unable to run on earlier versions of the library/API that do not support that feature. 通常,如果应用程序使用库/ API中的新功能,则无法在不支持该功能的库/ API的早期版本上运行。 Such applications would either fail to launch or crash when an attempt to use the feature was made. 当尝试使用该功能时,此类应用程序将无法启动或崩溃。 This problem cane be solved using weakly-linked symbols. 使用弱链接符号可以解决此问题。

... ...

A concrete example will help. 一个具体的例子会有帮助。 Suppose that someone uses a version of a JACK client we'll call "Jill". 假设有人使用JACK客户端版本,我们称之为“Jill”。 Jill was linked against a version of JACK that contains a newer part of the API (say, jack_set_latency_callback()) and would like to use it if it is available. Jill与包含API的新部分的JACK版本(例如,jack_set_latency_callback())相关联,并且如果可用则希望使用它。

When Jill is run on a system that has a suitably "new" version of JACK, this function will be available entirely normally. 当Jill在具有适当“J”版本的新系统上运行时,此功能将完全正常使用。 But if Jill is run on a system with an old version of JACK, the function isn't available. 但是如果Jill在具有旧版JACK的系统上运行,则该功能不可用。

With normal symbol linkage, this would create a startup error whenever someone tries to run Jill with the "old" version of JACK. 使用正常的符号链接,只要有人试图使用“旧”版本的JACK运行Jill,就会产生启动错误。 However, functions added to JACK after version 0.116.2 are all declared to have "weak" linkage which means that their abscence doesn't cause an error during program startup. 但是, 在版本0.116.2之后添加到JACK的函数都被声明为具有“弱”链接 ,这意味着它们的abscence在程序启动期间不会导致错误。 Instead, Jill can test whether or not the symbol jack_set_latency_callback is null or not. 相反,Jill可以测试符号jack_set_latency_callback是否为null。 If its null, it means that the JACK installed on this machine is too old to support this function. 如果为null,则表示此计算机上安装的JACK太旧而无法支持此功能。 If its not null, then Jill can use it just like any other function in the API. 如果它不为null,那么Jill可以像使用API​​中的任何其他函数一样使用它。 For example: 例如:

 if (jack_set_latency_callback) { jack_set_latency_callback (jill_client, jill_latency_callback, arg); } 

However, there are clients that may want to use this approach to parts of the JACK API that predate 0.116.2. 但是,有些客户可能希望将此方法用于 JACK API的早于0.116.2的部分。 For example, they might want to see if even really old basic parts of the API like jack_client_open() exist at runtime. 例如,他们可能想要查看 在运行时是否存在像jack_client_open()这样的API的真正旧基本部分。

Such clients should include <jack/weakjack.h> before any other JACK header . 此类客户端应在任何其他JACK标头之前包含<jack / weakjack.h> This will make the entire JACK API be subject to weak linkage, so that any and all functions can be checked for existence at runtime. 这将使整个JACK API受到弱连接的影响,因此 可以在运行时检查是否存在 任何和 所有函数。 It is important to understand that very few clients need to do this - if you use this feature you should have a clear reason to do so. 重要的是要 了解很少有客户需要这样做 - 如果您使用此功能,您 应该有明确的理由这样做。

[emphasis added] [强调补充]

This makes clear that a program, like yours, that takes the exceptional step of of including jack/weakjack.h for the purpose of weakening its references to the entire JACK API can be expected to run successfully only if it tests the definedness of every JACK API symbol before referencing it and handles the case in which it is not defined. 这清楚地表明,只有当它测试每个JACK的定义时,为了削弱对整个JACK API的引用而采取包括jack/weakjack.h在内的特殊步骤的程序才能成功运行。引用它之前的API符号,并处理未定义它的情况。 Your program does not conform. 你的程序不符合。 This one does: 这个做了:

myjack1.c 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;
}

and do does this one: 并做到这一点:

myjack2.c 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);
}

which sketches the usual approach to a discoverable API - apt for a program meant to install and run on a system that might not provide libjack at all. 它描绘了可发现API的常用方法 - 适用于在可能根本不提供libjack的系统上安装和运行的程序。 So you'd build it without reference to libjack like: 所以你建立它而不参考libjack如:

gcc -o myjack2 myjack2.c -ldl

and on Ubuntu 17.04 - which does provide libjack - it might run like: 而在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

So the library's T&Cs are in good order with respect to as-needed linkage. 因此,图书馆的条款与条件在按需连接方面处于良好状态。 That seems to leave you in a position of being independently dissatisfied that as-needed linkage works the way it does, rather than in a different way that would allow you to weaken all your references to the JACK API and still get libjack to be needed by your weak references to its API symbols:- 这似乎让你处于独立不满意的位置,即按需链接以它的方式工作,而不是以一种不同的方式允许你削弱你对JACK API的所有引用,并仍然需要 libjack您对其API符号的弱引用: -

I fail to see why a weak dependency is considered as no dependency at all. 我不明白为什么弱依赖被认为根本没有依赖。 For me, a weak dependency is to enable optional features. 对我来说,弱依赖是启用可选功能。 I do want these features to be enabled if possible, and the decision whether this is possible should be a runtime decision. 我确实希望在可能的情况下启用这些功能,并且决定这是否可行应该是运行时决定。 With the current behavior, it becomes a compile-time decision. 使用当前行为,它成为编译时决策。

Your view that a weak symbol reference gives rise to a weak linkage dependency on a library that defines the symbol does not have a footing for the GNU linker. 您认为弱符号引用导致对定义符号的库的链接依赖性的观点与GNU链接器没有关系。 A program depends on a library if its linkage needs a symbol definition that the libary provides; 如果程序的链接需要libary提供的符号定义,程序依赖于库; otherwise it doesn't depend on that libary: there aren't weak and strong degrees of dependency. 否则它不依赖于那个图书馆:没有弱的和强烈的依赖程度。 (The Darwin Mach-O linker does support a cognate distinction) (Darwin Mach-O链接器确实支持同源区别)

There are weak symbols , as opposed to the default and usual kind, which is strong . 弱的符号 ,而不是默认和通常的类型,这是强大的 {weak|strong} symbol is shorthand for {weakly|strongly} referenced symbol , since the same symbol may be referenced in multiple linker input files, sometimes or always weakly and sometimes or always strongly. {weak | strong}符号{weakly | strong} 引用符号的简写,因为在多个链接器输入文件中可能引用相同的符号,有时或总是弱,有时或总是强。

A strong symbol must have exactly one defining reference in the linkage. 强符号必须在链接中具有一个定义引用。

A weak symbol is such that: 弱符号是这样的:

  • The linker is not obliged to find a definition for it: it may remain undefined in the output file 链接器没有义务为它找到定义:它可能在输出文件中保持未定义

  • The linker is not obliged to fault multiple weak definitions of the same symbol in different input files. 链接器没有义务在不同的输入文件中对同一符号的多个弱定义进行故障。 If exactly one defining reference within the linkage is strong, then that strong definition is picked and all weak ones ignored. 如果链接中只有一个定义引用很强,则选择该强定义并忽略所有弱定义。 If all defining references in the linkage are weak then the linker will pick one at random. 如果链接中的所有定义引用都很弱,则链接器将随机选择一个引用。

From the first part of that it follows that an undefined weak reference to a symbol does not give rise to a linkage dependency at all . 从第一部分可以得出一个未定义的弱引用的符号不会产生联动依赖 A definition is not needed and the fact that a definition is not needed is the result of a decision by the programmer (eg #include <jack/weak_jack.h> ) or perhaps by the compiler. 不需要定义,并且不需要定义的事实是程序员(例如#include <jack/weak_jack.h> )或者编译器决定的结果。 It is not reasonable to expect that the linker, if directed to link only shared libraries that are needed , should then link libraries to furnish definitions of symbols for which you or the compiler have told it that definitions are not needed. 如果指向仅链接所需的共享库,则链接库应该链接库以提供您或编译器已告知它不需要定义的符号定义,这是不合理的。

If the linker were to behave like that in your case, that would constitute a linktime decision to freeze and enable an API which, by including jack/weak_jack.h , you have indicated you wish to reserve entirely for runtime discovery. 如果链接器行为类似于你的情况, 将构成链接时决定冻结和启用API,通过包括jack/weak_jack.h ,你已经表明你希望完全保留用于运行时发现。

Linking your problem program with -no-as-needed is successful as a way of smothering the bug in the program. 将您的问题程序与-no-as-needed链接成功是一种扼杀程序中的错误的方法。 The bug is that by including jack/weak_jack.h you commit yourself to runtime discovery of the whole API, but don't fulfil that committment and instead take the availability of the API for granted. 错误是通过包含jack/weak_jack.h你自己承诺整个API的运行时发现,但不履行该承诺,而是将API的可用性jack/weak_jack.h理所当然。 Hence the segfault with as-needed linkage. 因此,具有按需链接的段错误。 Linking with -no-as-needed just cancels the the effect of including jack/weak_jack.h . 使用-no-as-needed jack/weak_jack.h链接只会取消包含jack/weak_jack.h的效果。 Including it says your program doesn't need any of the API definitions: -no-as-needed says, whatever they are, you're getting them all anyway. 包括它说你的程序不需要任何API定义: -no-as-needed说,无论它们是什么,你无论如何都要得到它们。

In the light of the fact that all JACK APIs post version 0.116.2 are weakly defined without resort to jack/weak_jack.h , I think that you simply don't have any use for this header unless you are indeed planning a program that will do something worthwhile on a host from which libjack is missing. 鉴于所有JACK API发布版本0.116.2都是弱定义的,而没有求助于jack/weak_jack.h ,我认为你根本没有任何使用此标题,除非你确实计划一个程序将在缺少libjack的主机上做一些有价值的事情。 If you are planning that, then you have no alternative to runtime discovery of all the JACK APIs you use, regardless of linkage conventions, because you can't link libjack anyway. 如果您正在计划,那么除了链接约定之外,您无法使用所有JACK API的运行时发现,因为无论如何都无法链接libjack

If not, then just link libjack and, if you merely call jack_client_open , your program, on any host, will dynamically link all the API definitions, whatever they are on that host, because your reference to jack_client_open (in the absence of <jack/weak_jack.h> ) will make libjack needed , whether that matters to the linker that did the linking or not. 如果没有,那么只需链接libjack ,如果你只是调用jack_client_open ,你的程序将在任何主机上动态链接所有API定义,无论它们在主机上是什么,因为你对jack_client_open的引用(在没有<jack/weak_jack.h>的情况下) <jack/weak_jack.h> )将使libjack 必需 ,无论这对于进行链接的链接器是否重要。 If you want to be compatible accross API versions, then you need to implement runtime detection as documented of any API that is documented with the attribute JACK_WEAK_EXPORT - as opposed to JACK_OPTIONAL_WEAK_EXPORT, or JACK_OPTIONAL_WEAK_DEPRECATED_EXPORT : the latter denote fundamental APIs that can only be forcibly weakened via <jack/weak_jack.h> . 如果您希望兼容API版本,那么您需要实现运行时检测, 使用属性JACK_WEAK_EXPORT 记录的任何API所记录的 - 而不是JACK_OPTIONAL_WEAK_EXPORT, or JACK_OPTIONAL_WEAK_DEPRECATED_EXPORT :后者表示只能通过强制弱化的基本API <jack/weak_jack.h>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM