【问题标题】:Native Android - Can't locate undefined method when loading a shared lib even though previously loaded shared lib contains the definitionNative Android - 加载共享库时无法找到未定义的方法,即使先前加载的共享库包含定义
【发布时间】:2014-02-19 06:39:29
【问题描述】:

我正在构建一个 Android 应用程序,该应用程序在运行时加载到 2 个本机共享库中:1 个是使用其中未解析的符号构建的,另一个是解析并定义该符号的。在 Java 中,我首先加载定义符号的共享库,然后加载将符号声明为未解析的库,此时,运行时失败并显示: "无法加载库:reloc_library[]: 33 找不到 'someMethod'

所以这是一个独特的区别。具有未定义符号的共享库显然不知道其中包含符号定义的共享库。

我只是假设如果我首先加载带有方法定义的库,那么当我加载调用该方法的第二个库时,它将能够找到它。我错了吗?就我而言,似乎必须在两个本机库之间编译显式依赖项,这意味着(我认为)使用未解析的符号制作 .so 是没用的。

我一直在努力寻找类似的问题,但没有运气。我认为我的问题是由于架构限制,我正在考虑通过其他几种方式来解决它,但我想知道它是否可以简单地修复。

为了确保库本身不复杂,我创建了两个非常简单的 C 文件:

fcn_defined.c:
int someMethod()
{
  return 1;
}

fcn_undefined.c:
extern int someMethod();
int someOtherMethod()
{
  someMethod();
}

然后构建两个共享对象,其中 fcn_undefined.c 代码创建一个未定义 someMethod 的 .so,fcn_defined.c 构建一个定义了 someMethod 的 .so:

gcc -o libfcn_undefined.so fcn_undefined.c -shared -Wl,--export-dynamic
gcc -o libfcn_defined.so fcn_defined.c -shared -Wl,--export-dynamic

对这些进行 nm 会产生:
libfcn_undefined.so:

0001f08 d _DYNAMIC
00001fe8 d _GLOBAL_OFFSET_TABLE_
00002004 A __bss_start
         U __cxa_atexit
         U __cxa_finalize
00002000 d __dso_handle
00000290 t __on_dlclose
00002004 A _edata
00002004 A _end
000002a0 t atexit
000002b4 T someOtherMethod
         U someMethod

和 libfcn_defined.so:

00001f0c d _DYNAMIC
00001fec d _GLOBAL_OFFSET_TABLE_
00002004 A __bss_start
         U __cxa_atexit
         U __cxa_finalize
00002000 d __dso_handle
0000025c t __on_dlclose
00002004 A _edata
00002004 A _end
0000026c t atexit
00000280 T someMethod

所以你可以看到 someMethod() 是在 libfcn_defined.so 中定义的(它出现在 read elf dynsym 部分)并且在另一个 lib 中是未定义的。

如果有人对 readelf 输出感兴趣,我也可以添加。

在 Java 端,我在模拟器中单击了一个简单的按钮,它会创建一个包含以下内容的类:

static
{
        System.loadLibrary("fcn_defined");
        System.loadLibrary("fcn_undefined");
}

出于好奇,我在 fcn_undefined 编译行中添加了“-lfcn_defined”,并比较了 nm 和 readelf 的输出。 nm 的唯一区别是“T someOtherMethod”开始更远几个字节,而 readelf 的区别是 fcn_defined 的“NEEDED”行。这和我的预期差不多。 而且它不会像这样崩溃。

这几乎是完整的解释。我确实找到了一些关于 Android 如何强制您在 Java 中以反向依赖顺序加载库的详细信息,因为它在 LD_LIBRARY_PATH envvar 中没有引用您的应用程序的库路径(而是已经在 API 18 中修复)。不幸的是,由于市场渗透,我需要最低 API lvl 10 才能使用我的应用程序,其次我还是尝试了 API 19,但仍然失败。

如果我不得不猜测,我相信如果您没有明确告诉它在库 X 中查找符号,Android 就不支持查找符号。换句话说,因为我没有构建库 fcn_undefined 并显式依赖 libfcn_defined.so,所以 Android 无法解决它。有谁知道这是一个错误还是设计使然?这是正常的吗?如果是这种情况,您似乎无法选择创建带有未解析符号的 .so ,甚至更有趣的是,当您使用 ld 时,我用来构建它的 Android NDK 工具链默认具有此功能(它不会抱怨未解决),我尝试关闭该功能但似乎没有做任何事情,没有警告或错误生成库。

所以你可能会问,为什么我不只是编译 fcn_undefined 库并依赖于 fcn_defined 库。好吧,这进入了更大的架构讨论。我正在使用的代码(本示例中为 fcn_undefined.c)是一个 python 扩展,它使用用于 ARM 的交叉编译 python 工具链构建,我从 NDK 库调用这个库,所以现在 NDK 库依赖于 python在 Python 中具有未解析方法的模块,该方法在静态库中定义。将静态库链接到 NDK 共享库意味着我无法在 Java 中以正确的顺序加载本机共享库(由于前面提到的问题,它们已在 API 18 中修复)。我正在尝试使用现有系统,因为其他团队使用它,并且它用于为许多平台构建。 叹息我显然还有其他事情要弄清楚,但我希望至少能确定上面的那个。

【问题讨论】:

    标签: android android-ndk shared-libraries native


    【解决方案1】:

    您如此精美地展示的行为是设计使然(或缺乏,如果您愿意的话)。你是对的,crazy_linker 确实解决了一些这样的问题(但不是全部)。有一个简单但丑陋的解决方法。构建一个虚拟 libfcn_defined.so,它的 nm 中只有 T someMethod。使用它来链接libfnc_undefined.so,使用LD_LIBS。 NDK 会显示警告,但没关系。在你的 Java 中加载真正的libfcn_defined.so

    【讨论】:

    • 嗯...这是我没有想到的有趣方法。事实上,我需要做的就是创建一个空的 c 文件,将其设为“libfcn_defined.so”,然后链接到它。现在,不幸的是我在这里简化了我的问题太多,因为我实际上正在构建一个静态的 NDK 库链接了另一个 .so 需要的“已定义”符号,所以我有一个循环依赖,因为 NDK 库需要加载需要静态链接到 NDK 库的代码的辅助库。不过,这肯定会让我上路。谢谢!
    【解决方案2】:

    根据 Unix/ELF 设计,您需要在 libfnc_undefined.so 中添加一个 NEEDED 条目,将 libfnc_defines.so 列为依赖项,以便动态链接器查看缺失的符号。

    即您应该确保 -lfcn_defined(或 /path/to/libfcn_defined.so)出现在生成 libfcn_undefined.so 的链接命令中。

    如果您使用 ndk-build 生成这两个库,只需将 libfcn_defined.so 列为 libfcn_undefined.so 的 LOCAL_STATIC_LIBRARIES 或 LOCAL_SHARED_LIBRARIES 条目。

    如果您使用其他构建系统,请进行相应调整。

    【讨论】:

    • 感谢您的技术解答。我在你的答案和亚历克斯的答案之间左右为难,但他是解决我情况的最佳方法。感谢您的回答,我确实更好地理解了这个问题。正如我有些怀疑的那样,这是一个架构限制,不是我想象的 Android,而是 ELF。在我的构建系统中,由于其他一些限制,我无法在构建时链接完整的 .so ,1)它不存在。如果您需要链接定义它的库,为什么要允许未解析的符号?支持可能提到的亚历克斯周围的工作?再次感谢您的回答!
    • 我认为您实际上可以通过创建一个模拟版本的 libfcn_defines.so 来解决这个问题,它只定义您需要的符号(作为虚拟变量或函数),然后链接 libfnc_undefined.so 。跨度>
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-10-26
    • 2014-11-06
    • 2021-11-04
    • 2021-10-12
    • 2016-03-25
    • 1970-01-01
    相关资源
    最近更新 更多