请教一个动态链接库构建的问题 (c++) (android) (opencv) - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
nthhdy
V2EX    C++

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

  •  
  •   nthhdy
    workingenius 2020-12-24 19:57:00 +08:00 3240 次点击
    这是一个创建于 1801 天前的主题,其中的信息可能已经有所发展或是发生改变。

    各位大佬

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

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

    21 条回复    2020-12-26 13:55:08 +08:00
    GeruzoniAnsasu
        1
    GeruzoniAnsasu  
       2020-12-24 20:21:34 +08:00
    链接命令行里压根就没有 opencv 相关的库

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

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

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

    链接顺序的话,我试了把 libopencv_core.a 作为那些 .a 的最后一个,也是同样的错误
    mingl0280
        17
    mingl0280  
       2020-12-25 15:44:52 +08:00
    @nthhdy 因为有可能是引用错误啊……比方说你本来用的 std::string,但是因为 using 的关系匹配到了 cv::string 就炸了……
    nthhdy
        18
    nthhdy  
    OP
       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
        19
    mingl0280  
       2020-12-26 01:51:54 +08:00
    @nthhdy 我知道你啥意思,不过我真遇到过类似的坑爹问题,就是提一下。其实你还有个测试方法:你找到输出的.o 文件,然后手动运行一下链接命令,看看是不是即使手动链接也不认。如果是这种情况的话 90%以上可能性是你的库的 abi 不兼容,用 objdump 之类的检查一下……
    nthhdy
        20
    nthhdy  
    OP
       2020-12-26 03:00:32 +08:00
    @mingl0280 我就是把这条命令 copy 出来手动运行的啊。还不停地把它试着该来改去,加点参数、减点参数什么的。到现在还没成功。如果是 abi 不兼容,错误信息也不提示一下吗。。。而且应该不是 abi,明明各处都指定了 arm64-v8a 的。感觉这个问题还有其它我未知“维度”,再搜吧。

    跨平台编译链接要考虑的问题太多了,cpu 架构、指令集、操作系统约定、各层库版本、工具链、各种路径配置、各种编译链接细节,还有我好多叫得出名字但是不明所以的术语。越贴近底层,要了解的东西就越多。任何一个维度都有可能造成这个问题,还不清楚是哪一环出得问题。感觉这件事儿急不得,也没法“突击”,从基本的知识了解吧。
    mingl0280
        21
    mingl0280  
       2020-12-26 13:55:08 +08:00
    @nthhdy 你对 libopencv_core.a 做一个 objdump,然后 dump 出来的.o 文件跑一下 nm,仔细看一下是不是 CPU 架构不太一样。有时候真就这个问题。
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2641 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 29ms UTC 14:37 PVG 22:37 LAX 06:37 JFK 09:37
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86