I'm having trouble with using a library that contains weak-symbols and the --as-needed linker flag.
Example
(This uses the Jack library)
$ 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>). this is fine with me; I do want my symbols to stay weak. esp. my program is weakly linking against jack on OSX (-weak_framework Jackmp), which requires to include<jack/weakjack.h>When linking with
--as-needed, the linker excludes any library, that does not reference at least one non-weak symbol. 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
- some OSs (e.g. Ubuntu-16.04LTS) have
--as-neededenabled by default.
Now I think that --as-needed is a nice linker feature to get rid of many really unneeded runtime dependencies.
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.
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:
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). 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>. 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.
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, e.g. not including weakjack.h if the compiler has no equivalent for -weak_library or -weak_framework; but currently it seems that the closest I can get is something like #ifdef __APPLE__ which makes me shudder in this context).
So I'd really love some option to force libraries that only have weak symbols to be dylinked nevertheless.
Is there such a thing?
No, you're not.
Find out where your
libjack.sois, e.g.Then use
nmto examine the symbol types of the JACK API inlibjack.so:You'll find they are all type
T( = ordinary global symbol in text section:man nm). There are a few weak symbols in the library:But none of them are in the JACK API. Nothing you can do short of rebuilding your
libjack.sois going to change that. The right way to characterise your problem is:I'm having trouble linking a library with the
--as-neededlinker flag to a program in which I decided to weaken all my references to that libraryThe defining symbol references of the JACK API in
libjack.soare all strong. 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 oflibjack.soto provide their missing definitions.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. Since then, the Debian-derived distro-clan has followed suit while the RedHat/Fedora clan has stuck with the status quo ante.
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. 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>just defines macros that have that effect.It would be surprising if an old and major linux library like
libjackhas botched its adaptation to the as-needed schism. I suspect you've overlooked some of the small print aboutjack/weakjack.h:[emphasis added]
This makes clear that a program, like yours, that takes the exceptional step of of including
jack/weakjack.hfor 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. Your program does not conform. This one does:myjack1.c
and do does this one:
myjack2.c
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
libjackat all. So you'd build it without reference tolibjacklike:and on Ubuntu 17.04 - which does provide
libjack- it might run like: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
libjackto be needed by your weak references to its API symbols:-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. A program depends on a library if its linkage needs a symbol definition that the libary provides; 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)
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.
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 (e.g.
#include <jack/weak_jack.h>) or perhaps by the compiler. 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.Linking your problem program with
-no-as-neededis successful as a way of smothering the bug in the program. The bug is that by includingjack/weak_jack.hyou 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. Hence the segfault with as-needed linkage. Linking with-no-as-neededjust cancels the the effect of includingjack/weak_jack.h. Including it says your program doesn't need any of the API definitions:-no-as-neededsays, whatever they are, you're getting them all anyway.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 whichlibjackis missing. 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 linklibjackanyway.If not, then just link
libjackand, if you merely calljack_client_open, your program, on any host, will dynamically link all the API definitions, whatever they are on that host, because your reference tojack_client_open(in the absence of<jack/weak_jack.h>) will makelibjackneeded, whether that matters to the linker that did the linking or not. 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 attributeJACK_WEAK_EXPORT- as opposed toJACK_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>.