各位大佬
正在开发一个安卓 app,用到 jni,在 c++ 里调用 opencv 。但是 opencv 始终不能连接成功,链接时错误日志(截几行)如下:
/Users/rqs/proj/android_robot/build/intermediates/ndkBuild/release/obj/local/arm64-v8a/objs/native-lib/native_jni.o: In function `String':
/Users/rqs/proj/android_robot/opencv_prebuilt/sdk/native/jni/include/opencv2/core/cvstd.hpp:602: undefined reference to `cv::String::allocate(unsigned long)'
/Users/rqs/proj/android_robot/build/intermediates/ndkBuild/release/obj/local/arm64-v8a/objs/native-lib/native_jni.o: In function `Java_com_turingvideo_robot_jni_NativeHelper_drawText':
/Users/rqs/proj/android_robot/src/main/jni/native_jni.cpp:150: undefined reference to `cv::putText(cv::_InputOutputArray const&, cv::String const&, cv::Point_<int>, int, double, cv::Scalar_<double>, int, int, bool)'
/Users/rqs/proj/android_robot/build/intermediates/ndkBuild/release/obj/local/arm64-v8a/objs/native-lib/native_jni.o: In function `~String':
/Users/rqs/proj/android_robot/opencv_prebuilt/sdk/native/jni/include/opencv2/core/cvstd.hpp:648: undefined reference to `cv::String::deallocate()'
/Users/rqs/proj/android_robot/build/intermediates/ndkBuild/release/obj/local/arm64-v8a/objs/native-lib/native_jni.o: In function `String':
/Users/rqs/proj/android_robot/opencv_prebuilt/sdk/native/jni/include/opencv2/core/cvstd.hpp:602: undefined reference to `cv::String::allocate(unsigned long)'
/Users/rqs/proj/android_robot/build/intermediates/ndkBuild/release/obj/local/arm64-v8a/objs/native-lib/native_jni.o: In function `Java_com_turingvideo_robot_jni_NativeHelper_drawText':
/Users/rqs/proj/android_robot/src/main/jni/native_jni.cpp:151: undefined reference to `cv::putText(cv::_InputOutputArray const&, cv::String const&, cv::Point_<int>, int, double, cv::Scalar_<double>, int, int, bool)'
/Users/rqs/proj/android_robot/build/intermediates/ndkBuild/release/obj/local/arm64-v8a/objs/native-lib/native_jni.o: In function `~String':
/Users/rqs/proj/android_robot/opencv_prebuilt/sdk/native/jni/include/opencv2/core/cvstd.hpp:648: undefined reference to `cv::String::deallocate()'
/Users/rqs/proj/android_robot/build/intermediates/ndkBuild/release/obj/local/arm64-v8a/objs/native-lib/native_jni.o: In function `String':
/Users/rqs/proj/android_robot/opencv_prebuilt/sdk/native/jni/include/opencv2/core/cvstd.hpp:602: undefined reference to `cv::String::allocate(unsigned long)'
/Users/rqs/proj/android_robot/build/intermediates/ndkBuild/release/obj/local/arm64-v8a/objs/native-lib/native_jni.o: In function `Java_com_turingvideo_robot_jni_NativeHelper_drawText':
/Users/rqs/proj/android_robot/src/main/jni/native_jni.cpp:153: undefined reference to `cv::putText(cv::_InputOutputArray const&, cv::String const&, cv::Point_<int>, int, double, cv::Scalar_<double>, int, int, bool)'
/Users/rqs/proj/android_robot/build/intermediates/ndkBuild/release/obj/local/arm64-v8a/objs/native-lib/native_jni.o: In function `~String':
/Users/rqs/proj/android_robot/opencv_prebuilt/sdk/native/jni/include/opencv2/core/cvstd.hpp:648: undefined reference to `cv::String::deallocate()'
/Users/rqs/proj/android_robot/build/intermediates/ndkBuild/release/obj/local/arm64-v8a/objs/native-lib/native_jni.o: In function `String':
/Users/rqs/proj/android_robot/opencv_prebuilt/sdk/native/jni/include/opencv2/core/cvstd.hpp:602: undefined reference to `cv::String::allocate(unsigned long)'
我理解 undefined reference 就是链接时找不到某个符号的实现,一般是因为某个库的缺失、某个库的版本不对,类型不匹配。
我找到了构建工具执行的链接语句,也比较长:
/Users/rqs/Library/Android/sdk/ndk/21.3.6528147/toolchains/llvm/prebuilt/darwin-x86_64/bin/clang++\
-Wl,-soname,libnative-lib.so\
-shared\
/Users/rqs/proj/android_robot/build/intermediates/ndkBuild/debug/obj/local/arm64-v8a/objs-debug/native-lib/native_jni.o\
/Users/rqs/proj/android_robot/build/intermediates/ndkBuild/debug/obj/local/arm64-v8a/objs-debug/native-lib/src/arucotag.o\
/Users/rqs/proj/android_robot/build/intermediates/ndkBuild/debug/obj/local/arm64-v8a/objs-debug/native-lib/src/utils.o\
/Users/rqs/proj/android_robot/build/intermediates/ndkBuild/debug/obj/local/arm64-v8a/objs-debug/native-lib/src/occupancy_grid.o\
/Users/rqs/proj/android_robot/build/intermediates/ndkBuild/debug/obj/local/arm64-v8a/objs-debug/native-lib/src/occupancy_grid_jni.o\
/Users/rqs/proj/android_robot/build/intermediates/ndkBuild/debug/obj/local/arm64-v8a/objs-debug/native-lib/src/virtual_wall_grid_jni.o\
/Users/rqs/proj/android_robot/build/intermediates/ndkBuild/debug/obj/local/arm64-v8a/objs-debug/native-lib/src/virtual_wall_grid.o\
/Users/rqs/proj/android_robot/opencv_prebuilt/sdk/native/staticlibs/arm64-v8a/libopencv_aruco.a\
/Users/rqs/proj/android_robot/opencv_prebuilt/sdk/native/staticlibs/arm64-v8a/libopencv_highgui.a\
/Users/rqs/proj/android_robot/opencv_prebuilt/sdk/native/staticlibs/arm64-v8a/libopencv_calib3d.a\
/Users/rqs/proj/android_robot/opencv_prebuilt/sdk/native/staticlibs/arm64-v8a/libopencv_imgproc.a\
/Users/rqs/proj/android_robot/opencv_prebuilt/sdk/native/3rdparty/libs/arm64-v8a/libcpufeatures.a\
/Users/rqs/proj/android_robot/opencv_prebuilt/sdk/native/3rdparty/libs/arm64-v8a/libtegra_hal.a\
/Users/rqs/proj/android_robot/opencv_prebuilt/sdk/native/staticlibs/arm64-v8a/libopencv_core.a\
-lgcc\
-Wl,--exclude-libs,libgcc.a\
-Wl,--exclude-libs,libgcc_real.a\
-latomic\
-Wl,--exclude-libs,libatomic.a\
/Users/rqs/proj/android_robot/build/intermediates/ndkBuild/debug/obj/local/arm64-v8a/libc++_shared.so\
-target\
aarch64-none-linux-android21\
-no-canonical-prefixes\
-Wl,--build-id\
-ljnigraphics\
-nostdlib++\
-Wl,--no-undefined\
-Wl,--fatal-warnings\
-llog -ldl -lz -lm -lc -lm\
-o /Users/rqs/proj/android_robot/build/intermediates/ndkBuild/debug/obj/local/arm64-v8a/libnative-lib.so
拿错误里提到的cv::String::deallocate()
来说,我在我指定的 libopencv_core 这个静态库中能够找到这个实现,nm 显示如下:
[19:44:57] rqs:opencv_prebuilt git:(1e0923e*) $ nm -CA /Users/rqs/proj/android_robot/opencv_prebuilt/sdk/native/staticlibs/arm64-v8a/libopencv_core.a | grep deallocate | grep ' T '
/Users/rqs/proj/android_robot/opencv_prebuilt/sdk/native/staticlibs/arm64-v8a/libopencv_core.a:matrix.cpp.o: 0000000000000000 T cv::Mat::deallocate()
/Users/rqs/proj/android_robot/opencv_prebuilt/sdk/native/staticlibs/arm64-v8a/libopencv_core.a:stl.cpp.o: 0000000000000000 T cv::String::deallocate()
/Users/rqs/proj/android_robot/opencv_prebuilt/sdk/native/staticlibs/arm64-v8a/libopencv_core.a:umatrix.cpp.o: 0000000000000000 T cv::UMat::deallocate()
可见里面有代码段的全局的 cv::String::deallocate
,它是一个不接受参数的函数,所以不会因为参数类型不匹配导致整体不匹配。
既然提供了这个静态链接库,库里面也有它的实现,为什么还是会有链接错误呢?请指教,谢谢!
1
GeruzoniAnsasu 2020-12-24 20:21:34 +08:00
链接命令行里压根就没有 opencv 相关的库
你只 include 到了 cv 的头文件,没有指定链接库 |
2
pursuer 2020-12-24 20:28:23 +08:00
有符号不一定有实现吧,有的符号是外部符号,就没有实现
|
3
GeruzoniAnsasu 2020-12-24 20:32:39 +08:00
不好意思我瞎了 但看起来中间那几行把.a 文件跟.o 一起作为输入文件的地方不太正常。。。 感觉拼编译-链接参数的时候写得不对?
|
4
mingl0280 2020-12-25 01:59:55 +08:00
根据你这个编译结果……你找下你代码里面有没有用 using namespace std 和 std::string?
|
6
nthhdy OP @GeruzoniAnsasu 不好意思,命令太长,我也觉得贴出来太乱,有什么好办法吗? log 也没法高亮或者排版。
其实这个编译命令是工具自动生成的,不是我敲的,不知道它根据什么生成这样的。是 gradle 使用了 ndk build,后者又生成了这个命令。 |
8
nthhdy OP @GeruzoniAnsasu 读了读 ndk build 的源码,完全是 make file 语法,一堆 if else,超级多变量展开。makefile 本身可读性就有些低,这样一搞,读起来学习曲线还是挺大的。感觉是要把自己玩死。相比之下 gradle 用 groovy 语法就优雅多了。
|
9
wutiantong 2020-12-25 10:29:28 +08:00
@nthhdy 我怀疑你这个 arm64-v8a/libopencv_core.a 是 iOS 平台的编译产物
|
10
wutiantong 2020-12-25 10:32:54 +08:00
@wutiantong 即便它确实是 Android 的静态库,如果是二进制拿来用的话,ndk 的版本差异可能也会导致各种问题。
|
11
nthhdy OP @wutiantong 这个思路有道理。
但是 opencv 这些 .a 是我自己编译的,编译时特意指定了 abi 为 arm64-v8a 的(有可能是指定没生效?),ndk 的版本我确认是一致的。 请问检查平台是否一致,有什么方法、工具吗? |
12
wutiantong 2020-12-25 11:07:26 +08:00
@nthhdy Mac 的话可以用 file 命令查看一下 libopencv_core.a
|
13
waruqi 2020-12-25 12:18:03 +08:00
> 拿错误里提到的 cv::String::deallocate() 来说,我在我指定的 libopencv_core 这个静态库中能够找到这个实现,nm 显示如下:
命名空间不对哈,你 jni 里面 报 cv::String::deallocate 符号找不到, 你从库里面只找到 cv::Mat::deallocate,命名空间都不同,符号名都不一样,能链接通过才怪了。。 |
14
waruqi 2020-12-25 12:19:37 +08:00
当我没说,眼瞎,后面 看到 cv::String::deallocate 了。
|
15
kaler 2020-12-25 12:38:32 +08:00 via Android
-shared 是连接的命令吗?还有连接的顺序也可能造成这种问题。
|
16
nthhdy OP |
17
mingl0280 2020-12-25 15:44:52 +08:00
@nthhdy 因为有可能是引用错误啊……比方说你本来用的 std::string,但是因为 using 的关系匹配到了 cv::string 就炸了……
|
18
nthhdy OP @mingl0280 明白你意思,using 的确很容易出问题。使用一个变量时,它的实际的命名空间有可能跟想的不一样。
但是代码里的确用的是 cv::String,不是 std::string 。 再说 std::string 也没有 deallocate 方法,而且一个是 string ( s 小写)一个是 String ( S 大写),不会匹配错的。 另外,除了 String 类,还有许多别的类也 undefined reference,不会都是因为这个原因的。 我感觉 using 出的编译错误会更多,一般不会到链接这步。因为就算两个同名的类出现在两个不同的 namespace 里,它们的用法也基本上不会完全一致的,那单个文件编译都过不了。 |
19
mingl0280 2020-12-26 01:51:54 +08:00
@nthhdy 我知道你啥意思,不过我真遇到过类似的坑爹问题,就是提一下。其实你还有个测试方法:你找到输出的.o 文件,然后手动运行一下链接命令,看看是不是即使手动链接也不认。如果是这种情况的话 90%以上可能性是你的库的 abi 不兼容,用 objdump 之类的检查一下……
|
20
nthhdy OP @mingl0280 我就是把这条命令 copy 出来手动运行的啊。还不停地把它试着该来改去,加点参数、减点参数什么的。到现在还没成功。如果是 abi 不兼容,错误信息也不提示一下吗。。。而且应该不是 abi,明明各处都指定了 arm64-v8a 的。感觉这个问题还有其它我未知“维度”,再搜吧。
跨平台编译链接要考虑的问题太多了,cpu 架构、指令集、操作系统约定、各层库版本、工具链、各种路径配置、各种编译链接细节,还有我好多叫得出名字但是不明所以的术语。越贴近底层,要了解的东西就越多。任何一个维度都有可能造成这个问题,还不清楚是哪一环出得问题。感觉这件事儿急不得,也没法“突击”,从基本的知识了解吧。 |
21
mingl0280 2020-12-26 13:55:08 +08:00
@nthhdy 你对 libopencv_core.a 做一个 objdump,然后 dump 出来的.o 文件跑一下 nm,仔细看一下是不是 CPU 架构不太一样。有时候真就这个问题。
|