【问题标题】:Exclude dynamic dependencies from build command?从构建命令中排除动态依赖项?
【发布时间】:2019-01-14 12:21:21
【问题描述】:

假设我们有一个库 libutils.so:

ldd libutils.so
...
libdependency.so
...

让我们进一步假设我们需要构建一个应用程序:

g++ appliation.cpp -lutils -o application

我们可以在上面的命令中省略 -ldependency 还是必须写:

g++ appliation.cpp -lutils -ldependency -o application

【问题讨论】:

    标签: linux gcc ld ldd


    【解决方案1】:

    我们可以在上面的命令中省略 -ldependency

    如果你控制libutils.so本身的链接,是的,你可以。插图:

    ma​​in.c

    extern void foo(void);
    
    int main(void)
    {
        foo();
        return 0;
    }
    

    foo.c

    extern void bar(void);
    
    void foo(void)
    {
        bar();
    }
    

    bar.c

    #include <stdio.h>
    
    void bar(void)
    {
        puts(__func__);
    }
    

    我们将创建一个依赖于libfoo.so 的程序,该程序依赖于libbar.so。 制作目标文件:

    $ gcc -Wall -c -fPIC foo.c bar.c
    gcc -Wall -c main.c
    

    现在链接libbar.so 简洁的方式:

    $ gcc -shared -o libbar.so bar.o
    

    下一个链接libfoo.so这样:

    $ gcc -shared -o libfoo.so foo.o -L. -lbar -Wl,-rpath=$(pwd)
    

    -rpath linker option的效果是:

    -rpath=目录

    将目录添加到运行时库搜索路径。这在将 ELF 可执行文件与共享对象链接时使用。 所有 -rpath 参数都连接起来并传递给运行时链接器,运行时链接器使用它们在运行时定位共享对象。 -rpath 选项也用于定位明确包含在链接中的共享对象所需的共享对象; 请参阅 -rpath-link 选项的说明。如果在链接 ELF 可执行文件时未使用 -rpath, 如果定义了环境变量 LD_RUN_PATH 的内容将被使用。

    结果是:

    $ objdump -x -j .dynamic libfoo.so | egrep '(RUNPATH|NEEDED)'
      NEEDED               libbar.so
      RUNPATH              /home/imk/develop/so/scrap
    

    libfoo.so 在其.dynamic 部分中有一个NEEDED 条目,说明 该库对libbar.so 具有运行时依赖项。同样它有 那里有一个RUNPATH 条目,表示可以在/home/imk/develop/so/scrap 中搜索运行时依赖项 那只是我进行链接的pwd:不一定是这样,只要确实如此 当链接器或加载器来寻找它时,可以找到libbar.so 的目录。

    链接器可以读取此信息,当libbar.so 与其他内容链接时, 并在运行时由加载程序。所以最后我可以像这样链接prog

    $ gcc -o prog main.o -L. -lfoo -Wl,-rpath=$(pwd)
    

    -lbar 就不用提了,因为libfoo.so 本身就为链接器提供了 libfoo.so 依赖于 libbar.so 的信息,以及在哪里寻找它。

    由于我在prog的链接中也通过了-rpath=$(pwd),所以我们看到了prog 将提供信息

    $ objdump -x -j .dynamic prog | egrep '(RUNPATH|NEEDED)'
      NEEDED               libfoo.so
      NEEDED               libc.so.6
      RUNPATH              /home/imk/develop/so/scrap
    

    到运行时加载器:prog需要libfoo.so,可以找 在/home/imk/develop/so/scrap。当加载器找到libfoo.so并加载它时,它会 从中发现:

      NEEDED               libbar.so
      RUNPATH              /home/imk/develop/so/scrap
    

    然后会找到并加载libbar.so,这将使它能够解析所有 施工过程中引用的符号。因此,prog 可以立即运行:

    $ ./prog
    bar
    

    我没有prog 的链接中传递-rpath=$(pwd)。但如果我没有:

    $ gcc -o prog main.o -L. -lfoo
    $ ./prog
    ./prog: error while loading shared libraries: libfoo.so: cannot open shared object file: No such file or directory
    

    加载程序不知道在哪里可以找到libfoo.so。见:

    $ ldd prog
        linux-vdso.so.1 (0x00007ffffcc35000)
        libfoo.so => not found
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f4d1aff9000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f4d1b5ec000)
    

    然后我不得不求助于:

    $ export LD_LIBRARY_PATH=.
    $ ldd prog
        linux-vdso.so.1 (0x00007fff964dc000)
        libfoo.so => ./libfoo.so (0x00007fc2a7f35000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fc2a7b44000)
        libbar.so => ./libbar.so (0x00007fc2a7942000)
        /lib64/ld-linux-x86-64.so.2 (0x00007fc2a8339000)    
    $ ./prog
    bar
    

    稍后

    ldd libutils.so 的输出中是否存在 libdependency.so 是否足以在链接期间省略 -ldependencny 尚不清楚

    关于ldd utils.so:-

    1. ldd libutils.so 输出是否报告了别名 libdependency.so
    2. 如果 1 为“是”,它是否也会将该 so-name 解析为实际文件?

    如果 1 为否,则 libdutils.so 不包含有关其对 libdependency.so 的依赖性的信息 并且您必须在任何进一步的链接中指定-lutils -ldependency

    如果对 1 表示“是”但对 2 表示“否”(即ldd libutils.so 报告libdependency.so =&gt; not found),则libutils.so 有 一个名为libdependency.soNEEDED 条目,但不是一个RUNPATH 条目,链接器或 loader 可以将该名称解析为任何实际文件。在这种情况下,如果链接-lutils,则必须链接-lutils -ldependency,以便链接器然后搜索解析-ldependency 的文件。至少,只要在您进行链接时ldd libutils.so 仍然 报告libdependency.so =&gt; not found,您就必须这样做。继续阅读...

    如果 1 是“是”,2 是“是”,那么您可以在进一步的链接中删除 -ldependency,前提是它是 在你运行的相同环境中运行 ldd libutils.so

    这个警告是必要的,因为如果ldd libutils.so 解析为libdependency.so,你就知道 是ldd 能够使用加载程序的搜索算法解析libdependency.so:-

    • LD_LIBRARY_PATH 环境变量(在活动 shell 中)列出了一个目录 在其中找到libdependency.so,或
    • libutils.so 提供了一个 RUNPATH,其中找到了 libdependency.so,或者
    • libdependency.so 位于/etc/ld.so.conf(或其递归include-扩展)中列出的目录之一中,或
    • libdependency.so 位于加载程序的受信任搜索目录之一,/lib/usr/lib

    如果ldd 可以通过这四种方式之一解析libdependency.so,那么链接器将能够 以同样的方式进行操作,只要在您进行链接时该方式仍然成功

    回到我的例子和我的链接:

    $ gcc -shared -o libfoo.so foo.o -L. -lbar -Wl,-rpath=$(pwd)
    

    在那之后,感谢-rpath=$(pwd)。我可以链接prog 喜欢:

    $ gcc -o prog main.o -L. -lfoo
    

    不提-lbar,就成功了。现在我链接libfoo.so 而不是没有 -rpath:

    $ gcc -shared -o libfoo.so foo.o -L. -lbar
    

    之后:

    $ objdump -x -j .dynamic libfoo.so | egrep '(RUNPATH|NEEDED)'
      NEEDED               libbar.so
    

    不再有RUNPATH,因此:

    $ ldd libfoo.so
        linux-vdso.so.1 (0x00007ffda05e6000)
        libbar.so => not found
    

    因为加载器也无法以任何其他方式解析libbar.so

    现在如果没有-lbar,我将无法再链接prog

    $ gcc -o prog main.o -L. -lfoo
    /usr/bin/ld: warning: libbar.so, needed by ./libfoo.so, not found (try using -rpath or -rpath-link)
    ./libfoo.so: undefined reference to `bar'
    

    但如果我这样做:

    $ export LD_LIBRARY_PATH=$(pwd)
    

    然后:

    $ ldd libfoo.so 
        linux-vdso.so.1 (0x00007ffe56d1e000)
        libbar.so => /home/imk/develop/so/scrap/libbar.so (0x00007fd2456e8000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fd2452f7000)
        /lib64/ld-linux-x86-64.so.2 (0x00007fd245aec000)
    

    libfoo.so 的依赖 libbar.so 由加载器解析,使用 LD_LIBRARY_PATH,并在 链接器以同样的方式:

    $ gcc -o prog main.o -L. -lfoo; echo Done
    Done
    

    如果我再次清除LD_LIBRARY_PATH

    $ unset LD_LIBRARY_PATH
    $ gcc -o prog main.o -L. -lfoo; echo Done
    /usr/bin/ld: warning: libbar.so, needed by ./libfoo.so, not found (try using -rpath or -rpath-link)
    ./libfoo.so: undefined reference to `bar'
    collect2: error: ld returned 1 exit status
    Done
    

    回到失败。

    【讨论】:

    • 非常感谢。你的回答很详细。但在 ldd libutils.so 的输出中是否存在 libdependecy.so 是否足以在链接期间省略 -ldependecny 仍然有点不清楚。链接器搜索这个库,找不到就报错就够了吗?
    • @Alexey 查看扩展答案。
    猜你喜欢
    • 1970-01-01
    • 2014-12-02
    • 1970-01-01
    • 2021-08-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-30
    相关资源
    最近更新 更多