【问题标题】:undefined symbols when linking one .so with other将一个 .so 与另一个链接时未定义的符号
【发布时间】:2014-12-11 11:56:16
【问题描述】:

a.so 定义函数 A()b.so 定义函数 B() 并调用在 a.so 中定义的 A()b.so 链接到 a.so 如下

gcc -fPIC -shared B.c -o libb.so -la

现在我创建了一个调用 B() 的二进制文件,该二进制文件在 b.so 中定义

gcc mybin.c -o mybin -lb

gcc 正在检查 b.so 中的每个符号并抛出错误,指出 A() 未定义。

gcc mybin.c -o mybin -lb -la

上述方法有效,但我必须将 mybin 与 a.so 链接,即使它与 a.so 没有直接关系。我的要求是,如果 b.soa.so 正确链接,则将 mybinb.so 链接应该可以工作。

这可能吗??

【问题讨论】:

  • linking mybin with just a.so should work. 我希望你的意思是b.so...

标签: c linux gcc shared-libraries


【解决方案1】:

链接可执行文件时,链接器想知道libb.so 所需的库是否存在,以便检查它们是否解析libb.so 中的任何未定义引用

链接器在通常的位置查找所需的库,因此如果找不到liba.so,那么您可以使用LD_LIBRARY_PATH 告诉链接器在哪里查找,如另一个答案所示,或者使用链接器专门为此目的而存在的选项,-rpath-link

gcc mybin.c -o mybin -lb -Wl,-rpath-link=.

-Wl, 前缀是告诉 GCC 将选项传递给链接器的方式,这是 GCC 不直接知道的链接器选项所必需的。)

或者,您可以告诉链接器在 libb.so 中允许未定义的符号,在这种情况下,它不会尝试找到 liba.so 来解析对 A() 的引用,并相信您将正确链接并确保该库在运行时可用。这是通过--allow-shlib-undefined 选项完成的:

gcc mybin.c -o mybin -lb -Wl,--allow-shlib-undefined

但是,通常最好让链接器检查所有符号是否已定义,并告诉它如何找到所需的库,因为它会更快地发现真正的问题。

-rpath-link 选项only 告诉链接器在哪里寻找额外的共享库,它对链接的可执行文件没有可见的影响(即它不会在可在任何地方执行)。另一种选择是创建libb.so,以便它包含如何找到嵌入其中的liba.so 的知识。这是通过 -rpath 链接器选项完成的,例如如果liba.so/some/path 中,您可以这样做:

gcc -fPIC -shared B.c -o libb.so -la -Wl,-rpath=/some/path

这会在libb.so 中添加一个DT_RPATH 标记,链接器将使用它来查找其依赖项:

readelf -d libb.so  | fgrep RPATH
 0x000000000000000f (RPATH)              Library rpath: [/some/path]

现在您无需了解liba.so 即可链接可执行文件,链接器将看到libb.so 需要liba.so 并使用RPATH 找到它:

gcc mybin.c -o mybin -lb

如果路径/some/path/liba.so 是固定的,这可以正常工作,但在开发期间和部署可执行文件之后在同一位置找到库可能不是真的。在这种情况下,您仍然可以对可执行文件使用-rpath-link 来告诉链接器在链接期间在哪里寻找它,并依靠 RPATH 在运行时找到它。或者您可以在 RPATH 中使用特殊字符串 $ORIGIN,动态链接器将其扩展到包含 RPATH 的对象的位置,例如如果liba.solibb.so 始终在同一个目录中,您可以像这样链接libb.so(注意引号以防止shell 扩展$ORIGIN):

gcc -fPIC -shared B.c -o libb.so -la '-Wl,-rpath=$ORIGIN'

readelf -d libb.so  | fgrep RPATH
 0x000000000000000f (RPATH)              Library rpath: [$ORIGIN]

【讨论】:

  • 很好的答案。是否可以在创建 libb.so 时使用 -rpath-link(将 liba.so 链接到它)并仅使用 'gcc mybin.c -o mybin -lb' 创建 'mybin'
  • @tez,我已经更新了我的答案。简而言之,您为此使用-rpath-rpath-link 只是在链接时将目录添加到搜索路径中,而-rpath 将目录记录在可执行文件/库中
【解决方案2】:

缺少的位是LD_LIBRARY_PATH:

export LD_LIBRARY_PATH=.
gcc -fPIC -shared a.c -o liba.so
gcc -fPIC -shared b.c -o libb.so -la
gcc mybin.c -o mybin -lb -L.

-L 不起作用的原因似乎是它用于«将目录 dir 添加到要搜索的目录列表中 for -l »的这个标志,请参阅GCC doc。并且libba.so 没有在-l 中指定。

【讨论】:

    【解决方案3】:

    步骤如下:

    gcc -fPIC -shared a.c -o liba.so
    gcc -fPIC -shared b.c -o libb.so
    gcc mybin.c -o mybin -lb -la -L.    # -L to look for the directory to load .so files
    
    Then make sure you do:
    export LD_LIBRARY_PATH=<your_directory>
    

    你需要在 gcc mybin.c -o mybin -lb -la -L 中提到 liba.so 和 libb.so。因为函数 A() 需要在运行时加载。 b.so 不与 a.so 链接。链接发生在共享库中的运行时。它们被编译并且对函数或外部变量的任何引用都不会被解析。

    如果您想包含 b.so 但不包含 a.so,则将 b 编译为静态库,从而将 a 加载到其二进制文件中。然后当你编译 mybin 时,你只需要 b.lib

    【讨论】:

    • 很抱歉,这是 OP 要求的解决方案吗?
    • @Romaan 我的问题是如何在不将其链接到 a.so 的情况下创建“mybin”
    • 啊!感谢您清除 @tez,您需要在 gcc mybin.c -o mybin -lb -la -L. 中提到 liba.so 和 libb.so,因为函数 A() 需要在运行时加载。 b.so 不与 a.so 链接。链接发生在共享库中的运行时。它们被编译并且对函数或外部变量的任何引用都不会被解析。
    • 如果你想包含 b.so 但不包含 a.so 则将 b 编译为静态库,从而将 a 加载到其二进制文件中。然后当你编译 mybin 时,你只需要 b.lib
    • @Romaan 我希望你现在明白我的 cmets 的意思了。尽管如此,感谢您的努力。 :-)
    猜你喜欢
    • 1970-01-01
    • 2014-10-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-03-31
    • 2021-12-19
    • 2014-09-11
    • 2016-12-30
    相关资源
    最近更新 更多