一、背景
公司维护一批c++动态库,由于生产需要,每次更新都要在windows、linux_x86、kylin_arm等多个环境中编译一遍,操作比较麻烦,所以想通过交叉编译的方式在一台机器上边编译多个环境的动态库,减少工作量。考虑到工作难度以及本人水平,决定从官方发布的成品交叉编译工具链中选择合适的版本搭建一个交叉编译环境, 实现在linux_86平台编译linux_arm动态库。
搭建编译环境用ubuntu_x86虚拟机,动态库统一使用cmake构建编译,编译会用到一批第三方依赖库例如boost、opencv等等,既然我要生成能在kylin_arm上运行的动态库,那么这些编译过程中用到的三方库也必须是kylin_arm系统的。现在公司有一个完善的kylin_arm编译环境,所以为了节约成本,我就直接将环境中的三方库拷贝到ubuntu_x86虚拟机中,方便交叉编译的时候直接调用。
cmake中配置交叉编译工具链
, 这个比较简单,cmake可以制定gcc路径,那么我就用CROSS_COMPILE
这个参数做一个区分,在编译的时候指定它为true(cmake source/fiename -DCROSS_COMPILE=true
)那个cmake就会去交叉编译的路径下边查找gcc.
if(UNIX)
if(CROSS_COMPILE)
message(STATUS "CROSS_COMPILE:${CROSS_COMPILE}")
SET(CMAKE_SYSTEM_NAME Linux)
SET(CMAKE_SYSTEM_PROCESSOR arm)
SET(TOOLCHAIN_DIR "$ENV{HOME}/x86ToArm/gcc-arm-8.3-2019.03-x86_64-aarch64-linux-gnu")
set(CMAKE_C_COMPILER "${TOOLCHAIN_DIR}/bin/aarch64-linux-gnu-gcc")
set(CMAKE_CXX_COMPILER "${TOOLCHAIN_DIR}/bin/aarch64-linux-gnu-g++")
set(CMAKE_LINKER "${TOOLCHAIN_DIR}/bin/aarch64-linux-gnu-g++")
elseif (EXISTS "$ENV{HOME}/local/bin/gcc")
set(CMAKE_C_COMPILER "$ENV{HOME}/local/bin/gcc")
set(CMAKE_CXX_COMPILER "$ENV{HOME}/local/bin/g++")
set(CMAKE_LINKER "$ENV{HOME}/local/bin/g++")
elseif (EXISTS "/usr/local/bin/gcc")
set(CMAKE_C_COMPILER /usr/local/bin/gcc)
set(CMAKE_CXX_COMPILER /usr/local/bin/g++)
set(CMAKE_LINKER /usr/local/bin/g++)
endif()
endif()
message(STATUS "CMAKE_C_COMPILER is : ${CMAKE_C_COMPILER}")
message(STATUS "CMAKE_CXX_COMPILER is : ${CMAKE_CXX_COMPILER}")
二、问题1 can’t find “/lib64/libharfbuzz.so”
……
在编译的时候报错找不到一批这样的文件,于是我从现有的 kylin_arm里边拷贝过来放在交叉编译工具连里边。但是之后发现只有放在系统/lib64下边才能被找到,这显然还是有问题,我的交叉编译工具链我希望它是可以移植的。于是我考虑是不是我需要在cmake中设置交叉编译的
知识点:
(1)在交叉编译工具连中aarch64-linux-gnu/libc是系统根目录,libc/lib== lib, libc/usr/lib==/usr/lib
(2) 在cmake里边可以通过SET(CMAKE_SYSROOT "${TOOLCHAIN_DIR}/aarch64-linux-gnu/libc")
指定根路径, SET(CMAKE_FIND_ROOT_PATH "${CMAKE_SYSROOT}/lib64" "$ENV{HOME}/YEECOH_LIBS_Arm")
指定搜索路径
问题分析:cmake中设置了各种路径都不起作用,它还是顽强的去/lib64下边寻找,然后忽然发现报错的库都是opencv库间接使用的,那么可以想到/lib64的路径可能是由opencv库中的cmake写死了。检查发现果然在opencv-4.5.1/lib64/cmake/opencv4/OpenCVModules.cmake
中opencv将其调用的一些库路径写死了,于是将其修改为"\$<LINK_ONLY:harfbuzz>"
(这是opencv中统一的写法,也可将路径直接修改成交叉编译的路径。)
三、问题2 basic_ostringstream未定义的引用
分析:
(1) 交叉编译中自带的glibc版本是libstdc++.so.6.0.24, 麒麟编译使用的版本是0.28.
(2) 使用命令:nm -D /home/x86ToArm/gcc-linaro-7.3.1-2018.05-x86_64_aarch64-linux-gnu/aarch64-linux-gnu/libc/lib/libstdc++.so | c++filt | grep basic_ostringstream
查看报错的那个basic_ostringstream发现0.28版本中有定义,而0.24中没有定义。那么基本可以确定是版本不对的问题。 【参考链接:https://blog.csdn.net/Three_dog/article/details/104701644】
(3)检查发现/home/x86ToArm/gcc-linaro-7.3.1-2018.05-x86_64_aarch64-linux-gnu/aarch64-linux-gnu/lib64这个路径下边是交叉编译自带的libstdc++.so.6.0.24,交叉编译使用的应该是他, 从现有的kylin_arm环境中将0.28的版本拷贝过来替换掉交叉编译中的libstdc++.so.6.0.24(一套库),这个报错就没有了
四、问题3 libc.so 未定义的引用
知识点:
(1)libc.so: glibc, 是底层c库; listdc++.so: 底层c++库。
(2) listdc++.so可以直接通过替换动态库文件来升级版本;但是libc.so不能简单替换
,由交叉编译工具的制作过程可知,gcc与glibc是强关联的。
分析:
我在编译动态库的过程中是从现有的麒麟arm环境中拷贝了所需的一些底层库, 该麒麟环境有两套gcc: gcc9.3 + libc0.28 + libstdc++ 0.28 、gcc7.3+libc0.24+libstdc++0.24
。 其中有两个库是用libc0.28编译的。而我使用的交叉编译工具链是:gcc7.3+ glibc0.24+libstdc++0.24
。由于libc不能直接拷贝替换,那就只能考虑使用更高版本的交叉编译工具链。从官网下载了一个比较合适的版本:gcc8.3+libc0.28+libstdc++0.25
。将其中的stdc++动态可直接替换成0.28版本的,就可以了。最终生成的动态库可以在现有的麒麟arm环境中运行。