gpt4 book ai didi

gcc - 弱链接 vs "--as-needed"

转载 作者:行者123 更新时间:2023-12-02 01:27:20 28 4
gpt4 key购买 nike

我在使用包含弱符号和 --as-needed 的库时遇到问题链接器标志。

例子

(这使用 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

$

(示例代码被编辑为不再是段错误,因为段错误不是我的实际问题)

问题

问题似乎是:
  • Jack 将所有符号声明为弱符号(如果我包含 <jack/weakjack.h> )。这对我来说很好;我确实希望我的符号保持弱点。
    特别是我的程序与 OSX ( -weak_framework Jackmp ) 上的 jack 弱链接,这需要包含 <jack/weakjack.h>
  • 当与 --as-needed 链接时, 链接器排除任何不引用至少一个非弱符号的库。来自联机帮助页:

  • --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


  • 一些操作系统(例如 Ubuntu-16.04LTS)有 --as-needed默认启用。

  • 现在我认为 --as-needed是一个很好的链接器功能,可以摆脱许多真正不需要的运行时依赖项。

    但是,我不明白为什么弱依赖被视为根本没有依赖。对我来说,弱依赖是启用可选功能。如果可能的话,我确实希望启用这些功能,而这是否可能的决定应该是运行时的决定。对于当前的行为,它变成了编译时的决定。 (如果我想要那个,我会简单地通过一些预处理器魔法禁用相关代码)。

    一种解决方案显然是添加 --no-as-needed到链接器标志。
    我不想要这个:如果我的发行版(或编译我的二进制文件的人)认为这是要做的事情,我确实想摆脱过度链接。

    所以我可能会打开 as-needed在我已知的弱库中链接后:
      gcc -o myjack myjack.c  -Wl,--no-as-needed -ljack -Wl,--as-needed ...

    但这感觉也是错误的,因为我的强制需要的库之后的所有库都突然被强制到 --as-needed (这可能不是我的发行版或编译我的二进制文件的人认为这是要做的事情)。它似乎也给构建链添加了很多麻烦,只是因为某些库碰巧只导出弱符号。我不想手动跟踪所有执行此操作的库。

    我当然也可以简单地不包括 <jack/weakjack.h> .包含它的原因是因为该应用程序也可以在 OSX 上运行,我确实希望在其中选择依赖 JACK 框架(因此我与 -weak_framework Jackmp 链接),并在没有该框架的情况下保持我的程序可运行。

    我真的不想因为各种平台上的链接器之间的细微差别而弄乱我的应用程序代码。这可能是我遇到的主要问题:为什么我要向我的应用程序添加特定于平台的代码以适应不同的链接器细节 - 我可能会添加特定于功能的代码,例如不包括 weakjack.h如果编译器没有 -weak_library 的等效项或 -weak_framework ;但目前似乎我能得到的最接近的是 #ifdef __APPLE__这让我在这种情况下不寒而栗)。

    所以我真的很喜欢一些选项来强制只有弱符号的库被dylinked。

    有这样的事情吗?

    最佳答案

    I'm having trouble with using a library that contains weak-symbols and the --as-needed linker flag.



    不你不是。

    找出您的 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
    ...

    然后使用 nmlibjack.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

    你会发现它们都是类型 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 时遇到问题程序的链接器标志,其中
    我决定削弱我对该库的所有引用
    libjack.so 中 JACK API 的定义符号引用都很强。你有
    编写了一个程序,指示编译器在您的目标代码中发出以下符号
    对 JACK API 的弱 undefined reference ,并且您发现,通过按需链接,
    这些弱引用无法强制 libjack.so 的链接提供他们缺少的定义。

    it seems that the problem is:

    jack declares all symbols as weak (if I include ).

    when linking with --as-needed, the linker excludes any library, that does not reference at least one non-weak symbol.

    some OSs (e.g. Ubuntu-16.04LTS) have --as-needed enabled by default.



    最后两点是正确的。链接共享库的发行版之间的 split
    默认情况下需要的发行版和不会追溯到 Debian Wheezy,2013 年的发行版,
    其中 went over to as-needed .
    从那以后,衍生自 Debian 的发行版家族纷纷效仿,而 RedHat/Fedora
    部落一直坚持现状。

    第一点很困惑。 libjack.so ,正如我们所指出的,导出一个强定义的
    您无法通过编写和编译新代码来更改 JACK API。
    如果您包括 <jack/weakjack.h>在您的源文件之一中,那么您是
    在您的代码中声明所有 JACK API 符号为弱,编译器将
    为您提供一个仅包含对 JACK API 的弱引用的对象文件。 <jack/weakjack.h>只定义具有这种效果的宏。

    如果像 libjack 这样一个古老而主要的 linux 库,那将是令人惊讶的。
    已经拙劣地适应了按需 split 。我怀疑你忽略了 some ofthe small print about 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. 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. 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". 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.

    When Jill is run on a system that has a suitably "new" version of JACK, this function will be available entirely normally. But if Jill is run on a system with an old version of JACK, the function isn't available.

    With normal symbol linkage, this would create a startup error whenever someone tries to run Jill with the "old" version of JACK. 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. Instead, Jill can test whether or not the symbol jack_set_latency_callback is null or not. If its null, it means that the JACK installed on this machine is too old to support this function. If its not null, then Jill can use it just like any other function in the 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. For example, they might want to see if even really old basic parts of the API like jack_client_open() exist at runtime.

    Such clients should include <jack/weakjack.h> before any other JACK header. 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. 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.



    [强调]

    这清楚地表明,像您这样的程序采取了包括 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 的常用方法 - apt
    对于旨在在系统上安装和运行的程序
    可能不提供 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

    因此,图书馆的条款和条件在按需链接方面处于良好状态。那
    似乎让您处于独立不满意按需链接有效的位置
    它的方式,而不是让你削弱的不同方式
    您对 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.



    您认为弱符号引用会导致对库的弱链接依赖
    定义符号的符号没有用于 GNU 链接器的基础。一个程序
    如果链接需要库提供的符号定义,则取决于库;除此以外
    它不依赖于那个库:没有弱和强的依赖程度。 (达尔文 Mach-O 链接器确实支持同源区别)

    有弱符号,与默认和通常的类型相反,
    这是强大的。 {weak|strong} 符号是 {weakly|strong} 引用的简写
    符号,因为同一个符号可能在多个链接器输入文件中被引用,
    有时或总是微弱,有时或总是强烈。

    强符号在链接中必须恰好有一个定义引用。

    弱符号是这样的:
  • 链接器没有义务为它找到定义:它可能在输出文件中保持未定义
  • 链接器没有义务对同一符号的多个弱定义进行错误
    在不同的输入文件中。如果链接中恰好有一个定义引用是
    强,然后选择强定义并忽略所有弱定义。我摔倒
    在链接中定义引用是弱的,那么链接器将随机选择一个。

  • 从第一部分可以得出一个未定义的弱引用
    一个符号根本不会产生链接依赖。一个定义是
    不需要并且不需要定义的事实是
    由程序员决定(例如 #include <jack/weak_jack.h> )或由
    编译器。期望链接器(如果指向链接)是不合理的
    只有需要的共享库,然后才应该链接库以提供定义
    您或编译器告诉它不需要定义的符号。

    如果链接器在您的情况下表现得像那样,那将构成
    链接时决定卡住和启用 API,包括 jack/weak_jack.h ,
    您已表示希望完全保留用于运行时发现。

    将您的问题程序与 -no-as-needed 联系起来是成功的一种方式
    扼杀程序中的错误。错误在于包含 jack/weak_jack.h你致力于整个 API 的运行时发现,但不要实现这一点
    promise ,而是将 API 的可用性视为理所当然。因此
    具有按需链接的段错误。与 -no-as-needed 链接只是取消
    包含 jack/weak_jack.h的效果.包括它说你的程序没有
    需要任何 API 定义: -no-as-needed说,无论它们是什么,你都会得到
    反正他们都是。

    鉴于所有 0.116.2 版本后的 JACK API 都弱
    无需求助于 jack/weak_jack.h 的定义,我认为你根本就没有
    除非您确实正在计划一个程序,否则此标题有任何用处
    将在主机上做一些有值(value)的事情 libjack不见了。如果你
    正在计划,那么除了运行时发现所有
    您使用的 JACK API,无论链接约定如何,因为您无法链接 libjack反正。

    如果没有,那么只需链接 libjack并且,如果您只是拨打 jack_client_open ,
    您的程序,在任何主机上,将动态链接所有 API 定义,无论
    他们在那个主机上,因为你引用了 jack_client_open (在里面
    没有 <jack/weak_jack.h> ) 将使 libjack需要,无论是
    对进行链接与否的链接器很重要。如果你想兼容
    跨API版本,那么你需要实现运行时检测
    as documented
    任何 API documented具有 JACK_WEAK_EXPORT 属性
    - 而不是 JACK_OPTIONAL_WEAK_EXPORT, or JACK_OPTIONAL_WEAK_DEPRECATED_EXPORT :后者表示基本 API
    只能通过 <jack/weak_jack.h>强行削弱.

    关于gcc - 弱链接 vs "--as-needed",我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44967115/

    28 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com