请教一个动态链接库构建的问题 (c++) (android) (opencv)

2020-12-24 19:57:00 +08:00
 nthhdy

各位大佬

正在开发一个安卓 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,它是一个不接受参数的函数,所以不会因为参数类型不匹配导致整体不匹配。

既然提供了这个静态链接库,库里面也有它的实现,为什么还是会有链接错误呢?请指教,谢谢!

2553 次点击
所在节点    C++
21 条回复
GeruzoniAnsasu
2020-12-24 20:21:34 +08:00
链接命令行里压根就没有 opencv 相关的库

你只 include 到了 cv 的头文件,没有指定链接库
pursuer
2020-12-24 20:28:23 +08:00
有符号不一定有实现吧,有的符号是外部符号,就没有实现
GeruzoniAnsasu
2020-12-24 20:32:39 +08:00
不好意思我瞎了 但看起来中间那几行把.a 文件跟.o 一起作为输入文件的地方不太正常。。。 感觉拼编译-链接参数的时候写得不对?
mingl0280
2020-12-25 01:59:55 +08:00
根据你这个编译结果……你找下你代码里面有没有用 using namespace std 和 std::string?
nthhdy
2020-12-25 10:03:09 +08:00
@pursuer 这个符号类型是 T,也就是全局征文段,放在代码区的,应该有实现的。
nthhdy
2020-12-25 10:05:51 +08:00
@GeruzoniAnsasu 不好意思,命令太长,我也觉得贴出来太乱,有什么好办法吗? log 也没法高亮或者排版。

其实这个编译命令是工具自动生成的,不是我敲的,不知道它根据什么生成这样的。是 gradle 使用了 ndk build,后者又生成了这个命令。
nthhdy
2020-12-25 10:07:31 +08:00
@mingl0280 缺失的是 `cv::String` 的相关方法,不是 `std::string` 吧
nthhdy
2020-12-25 10:20:07 +08:00
@GeruzoniAnsasu 读了读 ndk build 的源码,完全是 make file 语法,一堆 if else,超级多变量展开。makefile 本身可读性就有些低,这样一搞,读起来学习曲线还是挺大的。感觉是要把自己玩死。相比之下 gradle 用 groovy 语法就优雅多了。
wutiantong
2020-12-25 10:29:28 +08:00
@nthhdy 我怀疑你这个 arm64-v8a/libopencv_core.a 是 iOS 平台的编译产物
wutiantong
2020-12-25 10:32:54 +08:00
@wutiantong 即便它确实是 Android 的静态库,如果是二进制拿来用的话,ndk 的版本差异可能也会导致各种问题。
nthhdy
2020-12-25 10:37:46 +08:00
@wutiantong 这个思路有道理。
但是 opencv 这些 .a 是我自己编译的,编译时特意指定了 abi 为 arm64-v8a 的(有可能是指定没生效?),ndk 的版本我确认是一致的。
请问检查平台是否一致,有什么方法、工具吗?
wutiantong
2020-12-25 11:07:26 +08:00
@nthhdy Mac 的话可以用 file 命令查看一下 libopencv_core.a
waruqi
2020-12-25 12:18:03 +08:00
> 拿错误里提到的 cv::String::deallocate() 来说,我在我指定的 libopencv_core 这个静态库中能够找到这个实现,nm 显示如下:

命名空间不对哈,你 jni 里面 报 cv::String::deallocate 符号找不到, 你从库里面只找到 cv::Mat::deallocate,命名空间都不同,符号名都不一样,能链接通过才怪了。。
waruqi
2020-12-25 12:19:37 +08:00
当我没说,眼瞎,后面 看到 cv::String::deallocate 了。
kaler
2020-12-25 12:38:32 +08:00
-shared 是连接的命令吗?还有连接的顺序也可能造成这种问题。
nthhdy
2020-12-25 13:14:35 +08:00
@kaler -shared 表示输出的是动态链接库,不是可执行文件

链接顺序的话,我试了把 libopencv_core.a 作为那些 .a 的最后一个,也是同样的错误
mingl0280
2020-12-25 15:44:52 +08:00
@nthhdy 因为有可能是引用错误啊……比方说你本来用的 std::string,但是因为 using 的关系匹配到了 cv::string 就炸了……
nthhdy
2020-12-25 18:21:23 +08:00
@mingl0280 明白你意思,using 的确很容易出问题。使用一个变量时,它的实际的命名空间有可能跟想的不一样。

但是代码里的确用的是 cv::String,不是 std::string 。
再说 std::string 也没有 deallocate 方法,而且一个是 string ( s 小写)一个是 String ( S 大写),不会匹配错的。
另外,除了 String 类,还有许多别的类也 undefined reference,不会都是因为这个原因的。

我感觉 using 出的编译错误会更多,一般不会到链接这步。因为就算两个同名的类出现在两个不同的 namespace 里,它们的用法也基本上不会完全一致的,那单个文件编译都过不了。
mingl0280
2020-12-26 01:51:54 +08:00
@nthhdy 我知道你啥意思,不过我真遇到过类似的坑爹问题,就是提一下。其实你还有个测试方法:你找到输出的.o 文件,然后手动运行一下链接命令,看看是不是即使手动链接也不认。如果是这种情况的话 90%以上可能性是你的库的 abi 不兼容,用 objdump 之类的检查一下……
nthhdy
2020-12-26 03:00:32 +08:00
@mingl0280 我就是把这条命令 copy 出来手动运行的啊。还不停地把它试着该来改去,加点参数、减点参数什么的。到现在还没成功。如果是 abi 不兼容,错误信息也不提示一下吗。。。而且应该不是 abi,明明各处都指定了 arm64-v8a 的。感觉这个问题还有其它我未知“维度”,再搜吧。

跨平台编译链接要考虑的问题太多了,cpu 架构、指令集、操作系统约定、各层库版本、工具链、各种路径配置、各种编译链接细节,还有我好多叫得出名字但是不明所以的术语。越贴近底层,要了解的东西就越多。任何一个维度都有可能造成这个问题,还不清楚是哪一环出得问题。感觉这件事儿急不得,也没法“突击”,从基本的知识了解吧。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/738701

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX