记一次X86到arm的代码迁移实践 |
您所在的位置:网站首页 › 软件包架构不匹配怎么回事 › 记一次X86到arm的代码迁移实践 |
1、背景
目前政企的软件,好多都要求进行国产化适配。项目上的代码也需要做国产化适配,主要是从X86_64+CentOS6.7系统移植到arm(鲲鹏)+银河麒麟V10系统,需在目标系统上编译出rpm包。这次移植,踩了很多坑,也缺乏代码移植相关的经验,希望能对正在做移植的开发人员有所帮助。 2、开始之前可以先搭建鲲鹏官方的代码迁移工具, 先大致分析一下代码是否有需要改动的地方以及改动的工作量。分析结果可适当参考,我的代码提示没有修改点,后面其实还是改了一些。 3、三方库编译开始移植的第一步,是先确认有哪些第三方库,所需的三方库都要先在新的系统上编译一遍。项目采用Makefile编译,打开Makefile文件查看: THIRD_DIR := $(ROOT_DIR)/3rd_party THIRD_LIB := boost jsoncpp THIRD_INC := $(foreach lib, $(THIRD_LIB), -I$(THIRD_DIR)/$(lib)/include) THIRD_LINK := $(foreach lib, $(THIRD_LIB), -L$(THIRD_DIR)/$(lib)/lib/linux/release) …… LDFLAGS +=……-ljson_linux-gcc-4.4.6_libmt -lssl -lboost_log_setup -lboost_log -lcurl -lboost_regex -lboost_thread -lboost_filesystem -lboost_system -lboost_program_options -lpthread可以看到,项目用到的三方库包括boost和jsconcpp。项目开始的时候是C++11刚发布的那几年,先在boost库已经纳入C++标准库了;jsoncpp用于解析json数据。 3.1boost库的编译 3.1.1boost库版本的确定boost库版本位于源代码目录下的version.hpp下, #define BOOST_VERSION 105400 …… #define BOOST_LIB_VERSION "1_54"很显然,项目的boost库版本为1.54。需要下载boost1.54的全包,利用里面的脚本文件生成b2,再用b2生成相应的库。boost每个版本的编译略有区别,请注意查看全包里面的index.htm. 3.1.2生成相应的库文件项目使用的静态库文件,只需生成.a文件即可。给出命令: ./bootstrap.sh --prefix=/usr/local/mdm/boost ./b2 install --libdir=/usr/local/mdm/boost/lib/release variant=release link=static threading=multi runtime-link=static --with-filesystem --with-program_options --with-regex --with-system --with-thread --with-date_time --with-test --with-log第一行代码生成b2文件,并制定输入路径; 第二行代码制定库存放路径、编译静态库以及所需的库。如果直接运行./b2也是可以的,会在代码路径下生成所有库动态库及静态库(.so、.a文件)。 编译完成后,将.a文件拷贝到项目的依赖包存放路径下。 3.2jsoncpp库的编译项目的jsoncpp版本为0.6.0,这个版本的jsoncpp编译需要依赖scons,scons解压可用: tar -zxvf scons-2.2.0.tar.gz安装好scons后,解压jsoncpp源码后,进入源码所在路径执行以下命令: python ../scons-2.2.0/script/scons platform=linux-gcc../scons-2.2.0/script/scons是scons安装路径,编译完成后libs/linux-gcc-7.3.0在即可得到相应库:libjson_linux-gcc-7.3.0_libmt.a、libjson_linux-gcc-7.3.0_libmt.so。 4、开始编译准备工作完成,可以开始编译。项目分为四个主要模块,通过Makefile文件,可以找出最先编译的模块进行编译。 …… APPS = $(SRC_DIR)/libutil $(SRC_DIR)/libcs $(SRC_DIR)/cs $(SRC_DIR)/test ……可以看到,项目会依次编译libutil、libcs、cs、test四个模块。 4.1第一个模块libutil的编译 4.1.1openssl依赖开始执行执行Makefile之后,很快得到了报错如下: …/3rd party/boost/include/boost/asi0/ss1/detail/impl/openssl_init.ipp:43:23: error: expected id-expression before '( token43 mutexes_resize(::CRYPTo_num_locks)); …… 麒麟V10: OpenSSH_7.8p1, OpenSSL 1.1.1d 10 Sep 2019麒麟V10确实安装了openssl,且版本更高。那为什么会编译不过呢?初次进行代码移植且对Linux系统只有浅薄理解;且受鲲鹏代码迁移工具分析结果的影响,认为不用修改代码的笔者在这个问题上折腾不少时间。编译都是依赖Makefile,那还是得从Makefile文件看起。 …… THIRD_DIR := $(ROOT_DIR)/3rd_party THIRD_LIB := boost jsoncpp THIRD_INC := $(foreach lib, $(THIRD_LIB), -I$(THIRD_DIR)/$(lib)/include) THIRD_LINK := $(foreach lib, $(THIRD_LIB), -L$(THIRD_DIR)/$(lib)/lib/linux/release) CFLAGS += -O2 -std=c++0x CXXFLAGS += -O2 -std=c++0x CPPFLAGS += -O2 -std=c++0x -I$(SRC_DIR)/libutil -I$(SRC_DIR)/libcs $(THIRD_INC) LDFLAGS += -lrt -L$(SRC_DIR)/libutil -L$(SRC_DIR)/libcs $(THIRD_LINK) -lcs -lutil -ljson_linux-gcc-4.4.6_libmt -lssl -lboost_system -lpthread ……可以看到,原先的Makefile脚本在编译的过程中通过-I -L两个参数定义了编译过程中头文件及库文件的寻找路径,上述Makefile里面的LDFLAGS同时指定了库文件需要寻找的库文件,如-lcs表示在库文件寻找路径下寻找libcs.a这个静态库。 以boost为例,-I表示的头文件搜寻路径为: $(ROOT_DIR)/3rd_party/boost/include-->/usr/include-->/usr/local/include同理,-L表示的库文件搜寻路径为: $(ROOT_DIR)/3rd_party/boost/lib--/lib-->/usr/lib-->/usr/local/lib详尽介绍可参考传送门。 查看下openssl下的文件结构: 第一个问题解决后,继续执行make命令,很快出现了新的问题,问题比较好处理。根据报错提示信息,是由于没有安装curl依赖造成,执行以下命令: yum install libcurl-devel libcurl-dev安装curl依赖即可。 4.1.3 模板匹配失败继续编译,报错如下: 编译报错并没有消失,只是错误信息从std::__cxx11::basic_string 变成了std::__1::basic_string,依然是模板推导失败。来看下代码实现: template std::string toJson(const T& val) { std::string ret; saveToJson(ret, val); return ret; }在不改变代码功能的情况下改成: template std::string toJson(const T& val) { Json::Value ret; saveToJson(ret, val); Json::StyledWriter writer; return writer.write(ret);//将Json::Value转成string }再次编译,这次终于编译通过了。至于为什么在高版本的gcc编译失败,推测是高版本的gcc对模板的类型检查要求更加严格导致。 4.1.4 openssl继续编译,又出现了以下错误提示: Makefile文件里加上openssl的依赖,再次编译成功生成了.O文件。到这,第一个模块libutil的编译算是完成了。 4.2 第二个模块libcs的编译在成功编译了libutil后,依赖关系已经处理好了,第二个模块没遇到什么问题便成功了。继续下个模块的编译。 4.3第三个模块cs的编译 4.3.1 libdl继续编译步骤,很快就报错如下: 这下好了,出现了几十个编译错误: 根据错误提示: undefined reference to symbol 'dlsym@@GLIBC_2.17参考这篇文章难道是glibc2.17里边没有定义dlsym?执行strings /lib64/libc.so.6 | grep GLIBC来查看两台服务器到底装了哪些版本的glibc。 CentOS: GLIBC_2.2.5 GLIBC_2.2.6 GLIBC_2.3 GLIBC_2.3.2 GLIBC_2.3.3 GLIBC_2.3.4 GLIBC_2.4 GLIBC_2.5 GLIBC_2.6 GLIBC_2.7 GLIBC_2.8 GLIBC_2.9 GLIBC_2.10 GLIBC_2.11 GLIBC_2.12 GLIBC_PRIVATE麒麟V10 GLIBC_2.17 GLIBC_2.18 GLIBC_2.22 GLIBC_2.23 GLIBC_2.24 GLIBC_2.25 GLIBC_2.26 GLIBC_2.27 GLIBC_2.28 GLIBC_PRIVATE可以看到,麒麟系统上最低版本的glibc也比CentOS的最高版本高了很多。难道真的是glibc高版本没有dlsym?试着下载glibc各版本的代码来检查,这个方法工作量颇大。仔细分析,如果是一个系统函数,不应该莫名其妙地没了或者变了,而更应该考虑自己的代码是否有问题。于是继续加上glibc的依赖,从那几十个报错信息中查找线索。 4.3.2 重新编译三方库
将库文件拷贝出来放到对应路径下后,和libboost_log_setup.a 库有关的报错没有了!看来真是三方库的编译有点问题。这样将boost的库一个个单独编译后,boost库的错误提示没有了。重新编译libjson,放到对应路径下编译通过。可能是最开始编译三方库的时候有什么操作步骤错了,编译的命令还是一样的。 4.4 第四个模块test的编译从代码实现来看,这个模块主要是一些单元测试代码。依然改了小部分代码,这里就不贴出来了。最后,执行生成rpm包的Makefile,成功生成了rpm包。 5、结语到这,编译算是通过了。接下来的任务就是要在arm架构的服务器上测试相关的业务逻辑。依然任重而道远。 |
今日新闻 |
推荐新闻 |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |