【问题标题】:Android NDK: how to link multiple 3rd party librariesAndroid NDK:如何链接多个 3rd 方库
【发布时间】:2012-11-30 00:53:31
【问题描述】:

假设我们正在构建一个共享库 A,它需要链接到 2 个外部静态库 B 和 C。您所拥有的只是 libB.a 和 libC.a,以及它们的头文件。

这是 libA 的简化 Android.mk:

LOCAL_LDLIBS := ../external/libB.a ../external/libC.a

include $(BUILD_SHARED_LIBRARY)

AFAIK,共享库的链接方式是:

  1. 抓取B和C的所有目标文件
  2. 删除 A 未引用的目标文件
  3. 解析 B 和 C 中的引用

这会导致链接错误,因为 B 和 C 相互调用,特别是它们调用了在第 2 步中被剥离的函数,因为 A 没有调用它们。

如果我们自己构建静态库,那么只需将 LOCAL_STATIC_LIBRARIES 替换为 LOCAL_WHOLE_STATIC_LIBRARIES,这样可以防止代码剥离(以牺牲代码大小为代价)。在后台,它将 --whole-archive 传递给链接器。

由于我们没有构建 B 和 C(甚至没有重建它们的源),有哪些选择?

  1. 手动引用 A 中缺少的函数,以免它们被剥离
  2. 弄清楚如何将 --whole-archive 传递给外部静态库的链接器
  3. 使用 PREBUILT_STATIC_LIBRARY(看到它提到,但从未使用过,根据文档,它在这种情况下听起来不适用)
  4. 构建可执行文件而不是共享库(不会以相同的方式剥离代码)
  5. 移动/重命名外部库以欺骗 NDK 构建系统认为它们是我的,以便我可以将它们添加到 LOCAL_WHOLE_STATIC_LIBRARIES。

我选择了选项 1,因为它是第一个有效的方法,但显然它不是很好。我在问是否有更好的解决方案。

这个问题的答案 (Linking issue when prebuilt static and shared libraries with the Android NDK) 让我想知道是否需要重新评估我的构建设置(链接到外部静态库的共享库)。我无法在那里发表评论,所以我在这里问了我自己的问题。

【问题讨论】:

  • 我会说选项 3 是要走的路,通过定义两个预构建模块(分别用于 B 和 C)。您能否详细说明为什么您认为它不适用于您的情况?否则,您可以通过提取对象(使用ar -x)并执行部分链接(使用ld -r -x)将B 和C 合并到一个静态库中。
  • 听起来不适用,因为它不符合文档所说的用途:“此功能可用于两件事:1/您想将自己的库分发给第三方NDK 开发人员无需分发您的源代码。2/ 您想使用自己的库的预构建版本来加快构建速度。”

标签: android android-ndk static-libraries


【解决方案1】:

答案可以在How to deal with recursive dependencies between static libraries using the binutils linker?中找到。

LOCAL_LDLIBS := -L ../external/ -lB -lC -lB

我采用了两个库的 NDK 示例,并进行了最小的更改来演示该技术 on GitHub

更新(2017):从2012年开始,NDK的规则变得更加严格,现在它会抱怨LOCAL_LDLIBS包含非系统库:

Android NDK:警告:Android.module:链接器标志中的非系统库:-la -lb
Android NDK:这可能会导致不正确的构建。尝试使用 LOCAL_STATIC_LIBRARIES
Android NDK: 或 LOCAL_SHARED_LIBRARIES 来代替列出
的库依赖 Android NDK:当前模块

这只是一个警告,所以你可以忽略它。

或在-l 之后添加空格以超越 NDK 保护:

LOCAL_LDLIBS := -L ../external/ -l B -l C -l B

或者,您可以使用

LOCAL_LDLIBS += -L ../external -Wl,--start-group -l B -l C -Wl,--end-group

如果所涉及的库不是预构建的,则无需猜测它们的位置(这在使用 Android Studio NDK 集成时可能特别棘手)。使用

LOCAL_LDLIBS := -L $(TARGET_OUT) …

存在一种替代方法,它不使用“递归”链接。但它涉及迭代。首先,尝试以通常的方式构建您的共享库。如果此操作因未解析的符号而失败,请将所有这些符号复制到剪贴板,然后将它们粘贴到您的 Android.mk 中。假设这些符号是 extBaextBbextBc在上面的场景中,我相信 libC 的某些对象找不到这些定义在libB,这就是链接失败的原因)。你现在需要的,添加

LOCAL_LDFLAGS += -Wl,-u'extBa' -Wl,-u'extBb' -Wl,-u'extBc'

您可以进行下一步,并将这一切与 libC 捆绑在一起:

LOCAL_EXPORT_LDFLAGS += -Wl,-u'extBa' -Wl,-u'extBb' -Wl,-u'extBc'

现在任何使用 libC 的共享库都不会错过这些符号。

【讨论】:

  • 谢谢,成功了。我将库列表两次添加到 LOCAL_LDLIBS(例如:MY_LIBS = A B C D E; LOCAL_LDLIBS += $(MY_LIBS) $(MY_LIBS) ),就像一个魅力!
猜你喜欢
  • 1970-01-01
  • 2011-06-13
  • 1970-01-01
  • 2017-02-05
  • 2015-04-06
  • 1970-01-01
  • 1970-01-01
  • 2011-11-14
  • 2013-12-30
相关资源
最近更新 更多