V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
nthhdy
V2EX  ›  C++

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

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

    各位大佬

    正在开发一个安卓 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   ·   实用小工具   ·   2409 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 28ms · UTC 02:18 · PVG 10:18 · LAX 18:18 · JFK 21:18
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.