APP下载

内网漫游过程中的编译方法研究*

2021-08-06蒋立兵韩隆隆

通信技术 2021年7期
关键词:内核嵌入式交叉

蒋立兵,申 秋,韩隆隆

(中国电子科技集团公司第三十研究所,四川 成都 610041)

0 引 言

在某些特殊网络内进行内网漫游的时候,能够触及甚至登录大量的设备或板卡,这些板卡通常运行的是嵌入式Linux系统,能够通过telnet或ssh方式登录。当需要在这些设备上运行一些常用的工具时,会面临一些比较棘手的问题,大致可分为如下几类。

(1)目标CPU架构为ARM、PowerPC、MIPS等嵌入式平台时,x86架构下编译的程序不能在目标上运行,而代码在目标平台也不能进行编译。

(2)目标机器采用uClibc时,通过常规ARM、MIPS等交叉编译工具编译出的程序,在目标机器上依旧不能运行。

(3)目标机器的内核版本很低,高版本内核下编译的程序不能在目标程序上运行。

本文从交叉编译工具生成、交叉编译和跨版本运行等方面进行阐述,能够解决大部分上述问题。

1 交叉编译

1.1 概 念

交叉编译是在嵌入式领域常用的一种编译方式,通过选择不同的交叉编译工具链,能够在当前平台下,编译出可运行于不同体系架构平台的程序[1]。例如在x86平台电脑上,使用Android Studio进行Android JNI开发时,会调用ARM的交叉编译工具链进行编译,编译好的软件能够在Android手机上运行。

使用交叉编译的主要原因如下:

(1)目标平台一般为低功耗设计,性能较低,运行速度较慢;

(2)编译过程需要消耗大量资源,嵌入式系统往往没有足够的内存和硬盘;

(3)完整的编译环境需要很多支持包,很多嵌入式系统是精简版Linux,难以满足需求。

1.2 交叉编译工具链

Linux编译过程包括了预处理、编译、汇编、链接等过程[2-3],如图1所示。交叉编译工具链(Toolchain)就是为了编译跨平台体系结构的程序代码而形成的一套完整工具集。它隐藏了预处理、编译、汇编、链接等细节。当指定了源文件(.c)和编译工具(GCC)时,它会按照编译流程调用不同的子工具,生成最终的可执行文件(ELF)。

1.3 交叉编译工具链获取

嵌入式目标平台类型众多,包括ARM、MIPS、MIPS64、MIPS64el、PowerPC、PowerPC64 等。常用平台的交叉编译工具可以通过网络下载,如ARM、MIPS等。但是当目标平台是使用的libc版本不匹配时或目标内核版本过低时,都可能导致编译出的程序不能正常运行,因此需要能够自行编译所需要平台交叉编译工具链。目前构建交叉编译工具链可通过crosstool-NG、Buildroot等工具进行制作。对比了crosstool-NG和Buildroot两款工具,其中crosstool-NG能够支持更老的内核和libc版本,对于内网漫游过程中遇到的目标机器适应性更强,作为使用首选。Buildroot与crosstool-NG的使用类似,可以根据实际情况灵活选择。本文使用crosstool-NG作为示例。

1.3.1 crosstool-NG获取

目前,crosstool-NG的最新版为1.24,本文示例选用1.10[4]版本,可以支持较老的内核和libc版本。

想要编译老版本内核的工具链,编译环境的选择也很重要,应尽量选用低版本操作系统,同时有相应的更新源支持,本文示例选用的操作系统为Ubuntu 12.04。

crosstool-NG源码下载后需要进行编译,编译时需要安装一些Linux组件,可以提前执行:

sudo apt-get install libncurses5-dev bison flex texinfo automake libtool patch gcj cvs cvsd gawk

然后按照常规的Linux编译方法,可以编译得到可执行文件ct-ng,如下所示。

root@ubuntu:~# ct-ng -v

GNU Make 3.81

Copyright (C) 2006 Free Software Foundation, Inc.

This is free software; see the source for copying conditions.

There is NO warranty; not even for MERCHAN TABILITY or FITNESS FOR A

PARTICULAR PURPOSE.

1.3.2 crosstool-NG使用

crosstool-NG内部集成了很多默认配置,执行ct-ng list-samples命令,可以查看支持的默认配置。如果配置刚好符合需求,可以直接使用,否则需要进行定制化修改。默认部分配置如下:

选择和配置参数需要根据目标CPU架构、系统、libc等信息进行判断。

CPU架构可以通过cat /proc/cpuinfo指令查看,根据CPU型号,判断CPU的架构类型,如ARM、MIPS64、MIPS64(el为小端模式)等,部分CPU信息如下:

系统信息可通过uname-a指令查看。根据目标系统的系统版本信息,选择相近的内核版本。系统信息如下:

$ uname -a

Linux localhost 4.19.81-perf-gb5c27fd #1 SMP PREEMPT Thu Aug 6 04:10:30 CST 2020 aarch64 Android

目标平台使用的是glibc还是uClibc,通过在目标平台查看/lib目录下的libc库文件进行判断,如果为libc.so.6,则使用glibc;如果为libuClibc-0.9.xx.xx.so,则使用uClibc。配置ct-ng时需要选用与目标系统uClibc版本相同的版本,图2中的uClibc版本为0.9.33.2。

本文通过PowerPC平台,展示参数配置过程。

(1)执行ct-ng powerpc-unknown-linux-gnu,配置成默认配置。

(2)执行ct-ng menuconfig进入配置界面,如图3所示。

(3)进入Target options页面,配置CPU参数,主要参数为Target Architecture(CPU架构)、Bitness(32位或64位)、Endianness(大小端),其他参数根据CPU单独进行配置,如图4所示。

(4)进入Option System页面,配置系统参数,主要参数为Linux Kernel Version,如图5所示。

(5)进入C Compiler页面,配置编译器选项,主要参数为gcc version,根据目标系统选择合适的GCC版本,如果需要编译C++代码,需要勾选C++选项,如图6所示。

(6)进入C library页面,配置libc选项,主要参数为C library(选择glibc或uClibc),*libc version(选择glibc或uClibc的版本),如图7所示。

配置完成后,可以执行ct-ng build,进行编译。编译过程中,需要下载很多源码包,但是由于选择的crosstool-NG版本太老,默认下载链接已经失效,需要手动下载。开始编译后,会在当前目录生成target文件夹,进入./target/tarballs/目录下,查看有tmp-dl结尾的文件,下载相应的包,然后拷贝到tarballs目录。大部分的包都可以在清华开源软件镜像站[5]进行下载。

完成所有的包下载后,开始进行编译,编译过程时间较长,编译过程中也可能出现各种各样的错误,一般是由于缺少库,或者配置错误,或者是操作系统和GCC版本不匹配等问题,可以通过搜索具体问题进行解决。由于编译过程的自由度颇高,可能出现的问题也较多,具体如何解决问题不在本文赘述。

编译完成后会在/home/(user)/x-tools/目录下生成相应的工具链,其中/bin目录下包含了gcc、g++、as等交叉编译工具,如图8所示。

1.3.3 交叉编译工具链使用

将生成的交叉编译工具链所在的bin文件目录加入系统环境变量,可以直接调用相应的编译工具进行编译,也可在使用Makefile时直接使用。如未加入环境变量,则需要指定绝对路径,例如,/home/test/x-tools/powerpc-unknown-linux-gnu/bin/powerpc-unknown-linux-gnu-gcc。

编译完成ELF文件可以通过file指令查看文件的基本信息,同时可以将工具上传到目标机器上进行使用验证,如果仍然不能正常运行,则需要检查libc版本是否正确、Linux内核是否支持等问题,调整参数后重新编译工具链。

2 低版本系统兼容运行高版本程序

在内网漫游过程中,还存在一种较为特殊的情况。目标系统是x86或x64平台,但系统内核比较老,libc库所支持的版本也比较老。在较高版本编译环境下编译出的ELF文件,在目标平台运行是,可能出现如图9所示类似的错误。

图9中出错的原因在于,源码中使用了高版本libc包含的库函数,而目标平台的libc版本较低,不支持这些库函数,从而导致没有办法正常运行。解决方案有两种,一是修改程序源码,找到使用高版本库函数的地方,就行修改和替换;二是将高版本的libc库文件上传到目标,通过修改可执行文件的lib库依赖路径,使得能够在目标平台上正常运行。修改源码的方式对较为简单的代码比较实用,但对于较为复杂的程序需要选用第二种方式,下面介绍如何实现。

首先需要安装patchelf工具,Ubuntu可以通过apt-get install patchelf指令进行安装。

patchelf 是一个用来修改ELF程序的小工具,可以修改动态链接库的默认搜索路径rpath。

在编译平台上,建立新文件夹mylib,然后使用ldd指令查看编译好的程序所需的库文件,然后将所有依赖的库文件拷贝到mylib文件夹,如下:

root@myuser: ldd dropbear

linux-vdso.so.1 => (0x00007fffd25d2000)

libutil.so.1 => /lib/x86_64-linux-gnu/libutil.so.1(0x00007fd429d6b000)

libz.so.1 => /lib/x86_64-linux-gnu/libz.so.1(0x00007fd429b51000)

libcrypt.so.1 => /lib/x86_64-linux-gnu/libcrypt.so.1 (0x00007fd429919000)

libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6(0x00007fd42954f000)

./mylib/ld-2.15.so => /lib64/ld-linux-x86-64.so.2(0x00007fd42a1af000)

通过patchelf修改编译好的ELF文件的rpath,可以将文件的默认链接库文件地址指向mylib文件夹。首先执行patchelf --set-rpath ./mylib dropbear,同时需要修改指定与libc配套的ld.so文件,执行patchelf --set-interpreter ./mylib/ld-2.15.so。执行成功后,其次通过ldd指令查看动态库链接情况,程序所需的库已指向当前目录下的mylib文件夹,如下:

root@myuser: ldd dropbear

linux-vdso.so.1 => (0x00007ffcd7bef000)

libutil.so.1 => ./mylib/libutil.so.1 (0x00007 ff046340000)

libz.so.1 => ./mylib/libz.so.1 (0x00007ff046129000)

libcrypt.so.1 => ./mylib/libcrypt.so.1 (0x00007 ff045ef0000)

libc.so.6 => ./mylib/libc.so.6 (0x00007 ff045b30000)

./mylib/ld-2.15.so => /lib64/ld-linux-x86-64.so.2(0x00007ff046784000)

将编译好的ELF文件以及mylib文件夹上传到目标平台上,放在同一目录下,即可成功运行。需要注意的是,ELF文件有最低内核版本的限制,目标平台的内核版本过低的话,也会导致运行不成功。

3 结 语

本文针对在特殊网络内进行内网漫游时,对多种平台的板卡、终端设备空有权限而无法运行自己的常用工具的情况,提供了两种解决方案:针对较老的内核和较为少用的CPU平台,使用crosstool-NG工具,制作相应的交叉编译工具链;针对内核版本过低的x86-64平台,使用修改动态链接库的方式,让编译好的ELF文件连接高版本的lib库,从而能够正常运行。

猜你喜欢

内核嵌入式交叉
基于IMX6ULL的嵌入式根文件系统构建
多内核操作系统综述①
菌类蔬菜交叉种植一地双收
Focal&Naim同框发布1000系列嵌入式扬声器及全新Uniti Atmos流媒体一体机
强化『高新』内核 打造农业『硅谷』
活化非遗文化 承启设计内核
基于ARM嵌入式的关于图像处理的交通信号灯识别
“六法”巧解分式方程
微软发布新Edge浏览器预览版下载换装Chrome内核
TS系列红外传感器在嵌入式控制系统中的应用