如何正确地使用头文件设置动态库加载?

3

这个标题很难搞清楚,让我解释一下我的情况:

另一个团队开发了一个库。他们提供了一个头文件和一个 *.so 文件。我们可以使用这个头文件并在自己的代码中包含它,如果我们希望的话。然而,*.so 文件是随着我们运行的平台一起提供的。我们无法在软件构建时获得这个 *.so。因此,我们也不能真正使用头文件,因为链接器会期望在某个时候可用这个 *.so

现在我所做的是创建一个包装类,在运行时加载 *.so 文件,然后使用 dlsym() 按名称查找函数,并将它们映射到函数指针。

这是唯一的选择吗?我能否使用头文件,但告诉链接器不要在构建时解析符号,而是在我们有机会加载 *.so 文件后再尝试解析它们呢?

请注意,真正的平台是Android(通过NDK),但希望通用的Linux建议在这种情况下也适用,因为我们有POSIX API可用。

2个回答

1
在Windows中,这个问题通过要求链接器需要导出库(.lib)而不是真正的动态库(.dll)来解决。我认为你可以尝试制作类似的东西,即制作一个包含所有从真正的.so导出的存根方法的虚假.so,并链接它。这有望使链接器满意,同时在运行时应用程序将加载真正的.so。

这就是NDK与平台库的交互方式,供参考。 - Dan Albert

1
您有几个选项,按优先顺序排列如下:

  1. 从维护者处获取库。仅提供头文件而不提供库(至少像我们在 NDK 中为库所做的那样提供存根库)是行不通的。
  2. 构建自己的存根库。如果您有要公开的符号列表,则非常简单。对于所有需要公开的变量和函数,在 C 文件中放置 int foo; void bar() {} 并将其构建为共享库。如果您在版本脚本中具有符号列表,您可能可以使用 Android 的 gen_stub_libs.py 来为您完成此操作。
  3. 在头文件中使用 __attribute__((weak)) 标记所有符号。链接器不会抱怨它们丢失了。如果在运行时缺少它们,库仍将加载,但每个函数的地址将为 nullptr。在大多数情况下,这并不是您想要的,因为如果库的定义错误,则会将构建时间失败转换为运行时失败,但在某些情况下,这可能很方便,因为使用 if (foo) { foo(); } 检查函数可用性比使用 dlsym 更容易。
  4. -Wl,--allow-shlib-undefined 添加到您的 ldflags 中。这比第 3 点更糟糕,因为它会影响您链接的所有库,但它不需要您干预头文件。

我最喜欢的是#1,缺点是现在每次他们发布新版本时,我都必须确保获取最新的库,而不是通过设备上的固件发布“自动”获取它。 - void.pointer
理想情况下,他们会在头文件旁边提供给你。 - Dan Albert
我的意思是,大多数情况下公共接口不会改变,只有实现方式会改变。所以我应该无论如何都升级它,还是要知道哪些版本会产生ABI不兼容性,只升级那些版本? - void.pointer
只要ABI不改变,您就不需要更新存根。然而,如果确实发生更改,每次获取更新的存根都会保护您免受意外的影响。 - Dan Albert

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接