在Android平台使用SNPE应链接libc++库
在android平台使用snpe库以前都什么太大问题,最近因为要使用userbuffer,编译时报链接错误:
undefined reference to `zdl::dlsystem::tensorshape::tensorshape(std::vector<unsigned long, std::allocator<unsigned long> >)
因为是链接错误,而且查看了头文件,tensorshape有对应的重载构造函数,所以怀疑snpe的库文件libsnpe.so不对。怀疑高通提供的库文件不对,好像这也不太可能,一时间陷入了困境。
开始从高通的客服那边没有得到太有用的信息,只能自己想办法。用objdump从库文件dump出符号表看看有没有对应的函数。但是高通release库肯定是把符号表去掉了的,只能通过objdump -t出来动态符号。从动态符号中筛选出和tensorshape相关的,然后找一些看着可能像的符号,用c++filt还原出函数名。
先在x86_64平台的libsnpe.so找到了
c++filt _zn3zdl8dlsystem11tensorshapec1est6vectorimsaimee zdl::dlsystem::tensorshape::tensorshape(std::vector<unsigned long, std::allocator<unsigned long> >)
这和我们需要的函数是一样的,也就是说x86_64平台的libsnpe.so肯定是包含这个重载的函数实现的。那android平台的不应该没有啊?继续在android平台的libsnpe.so里找,终于找到
c++filt _zn3zdl8dlsystem11tensorshapec2enst6__ndk16vectorimns2_9allocatorimeeee zdl::dlsystem::tensorshape::tensorshape(std::__ndk1::vector<unsigned long, std::__ndk1::allocator<unsigned long> >)
看着像,但是和平常见到的是不太一样,vector的定义是std::__ndk1::vector。这说明snpe链接的是llvm的标准libc++库。这样原因清楚了。我们编译使用的是cmake,最开始在build.gradle文件中并没有指定android_stl变量,编译使用的应该不是libc++库,导致了链接错误。(按照ndk文档的说法,android_stl默认的是c++_static,这是libc++的静态库。按理是能够链接通过的,实际也试过了,android_stl指定c++_static是能够编译通过的。但是不指定,就会出现上面的undefined reference错误。我怀疑默认使用的是gnu的库)
因为还使用了opencv的库(用的是opencv3的版本),如果改用libc++,会出现链接opencv出错。查找opencv的文档,opencv4.0.0开始使用c++11库,因此改用opencv4。在build.gradle文件中加上android_stl定义,编译通过。
android { defaultconfig { externalnativebuild { cmake { arguments '-dandroid_stl=c++_shared' } } } }
按ndk的文档,从ndk r18开始,libc++成为ndk中唯一可用的stl。因为我们使用的ndk版本还比较低,所以开始并没有出错。随着ndk升级,不仅r18开始libc++是唯一的stl,而且gcc也不再支持,只能使用clang。还是慢慢转过去了,以免出现一些莫名其妙的问题。