聊聊汽车ECU嵌入式开发中的交叉编译

最近参与了一个小项目,为某OEM提供someip协议栈,以实现整车的以太网通信。作为中间件供应商,我们开发了能够运行于各种操作系统和硬件平台下的someip协议栈。比如,操作系统有Android、QNX或Linux,硬件平台有Arm和X86。

 

在这个项目的过程中,我对交叉编译的底层原理简单研究了下,写篇总结文章把最近的所思所想分享出来。

 

有些朋友可能会有疑问,OEM单独采购的someip协议栈,如何部署在车上,以实现车内以太网通信呢?

 

在新车型开发阶段,OEM向中间件供应商提供各ECU的SDK(软件开发工具包)。中间件供应商使用这些SDK编译someip协议栈的源代码,并将编译生成的someip文件分发给ECU供应商。ECU供应商将生成的someip文件集成到自家的软件平台内部即可。

 

以arm-linux平台为例来说,ECU供应商获得了someip协议栈文件,比如进程文件、动态库文件、头文件和配置文件等。

 

进程文件放到/usr/bin目录下,库文件放到/usr/lib下,头文件放到/usr/include下。这些文件的放置路径并非唯一,在此仅用于举例。

 

接着,修改源码的CMakeLists.txt文件,改变代码的编译行为。比如,include_directories(/usr/include)用于编译预处理过程中引用的头文件,target-link-libraries(${project_name} someip.so)用于链接库文件。

 

通过这种方式,软件开发人员只需要调用someip提供的接口,就可以实现someip通信。

 

那么问题来了,我在个人电脑上编译的someip协议栈,为什么能够运行在嵌入式设备中呢?

 

这就回归了本文的主题交叉编译。百度百科对交叉编译的定义是在一个平台上生成另一个平台上的可执行代码。换种说法,就是在个人电脑(x86平台)下编译生成嵌入式设备(arm平台)的可执行代码。

 

为了讲解交叉编译,我们首先需要理解源码编译生成可执行文件的步骤。参考《程序员的自我修养》,以编译C代码为例,编译过程大致可以分为以下步骤:

  • 编译预处理:替换所有的宏定义,并展开源文件所需包含的头文件
  • 汇编:将c代码翻译成对应的汇编代码
  • 编译:将汇编代码编译为二进制文件
  • 链接:链接二进制文件需要的静态库
  • 加载:运行二进制文件时,装载器加载对应的动态库

 

按照我的理解,交叉编译是在编译阶段出现不同,即将汇编代码编译为二进制。了解计算机组成原理的朋友们应该知道,我们常说的x86架构与arm的不同主要体现在底层指令集

 

x86架构采用的指令集属于CISC(复杂指令集),而arm架构采用的指令集属于RISC(精简指令集)。指令集的主要作用是定义了汇编指令与二进制机器码之间的映射。因此,编译器按照指令集的不同将汇编代码翻译为不同的二进制机器码。

程序运行时,cpu读取一条条指令,并根据指令集制定的规则进行指令译码,并执行相关的操作。

 

简单地说,交叉编译生成的文件无法运行在PC机上,是由于底层指令集不同,导致cpu在指令译码时找不到正确的指令集。

 

同时,这也解释了为什么英特尔的CPU与AMD的CPU都可以运行windows系统,且能够安装相同的软件。

 

我们对交叉编译做进一步定义可得,在A平台上使用B平台的编译器,将源代码翻译为B平台使用的指令集的文件,且该文件只能在另一个平台上运行。

 

通过上面的分析,我们知道交叉编译必须通过对应的编译器才能完成。那么我们该如何指定编译器呢?

 

我们可以通过设置交叉编译的环境,实质就是指定编译器、链接器、SDK路径。。。等环境变量,类似下图这种形式。

上面说了一些理论性的知识,有些朋友理解起来会有些难度。接下来,我通过编译libxml的x86版本和arm版本,给大家展示下交叉编译

 

前期准备:

1. 准备linux环境,虚拟机或者WSL都可以。推荐WSL,安装简单,使用方便

2. 在linux环境下,安装arm-linux-gcc编译器,参考了解:https://blog.csdn.net/wu10188/article/details/86542418

3. 下载libxml源码压缩包,并在linux环境下解压

 

编译x86版本的libxml

环境准备好后,我们在linux环境下进入libxml的源码中。查看README文档,可以看到linux环境下的编译命令,其默认平台为x86。

 

为了便于大家理解,我在libxml目录下创建了install/x86目录,用于安装libxml编译生成的文件。

接着,输入以下两条命令:

./configure --prefix=/libxml/install/x86 --with-python=no --without-zlib --without-lzma
make install

之后进入install/x86目录下,我们可以看到自动生成了bin、lib和include的文件夹。如果上述命令中,不设置--prefix的路径,那么会默认安装到/usr/local/路径下面。

 

进入install/x86/bin目录下,使用file和objdump命令查看生成的bin文件,可以看到文件属性均为x86_64的版本。

编译arm版本的libxml

在install目录下创建arm文件夹,并输入以下两条命令:

./configure CC=arm-linux-gnueabihf-gcc --host=arm-linux --prefix=<libxml的绝对路径>/libxml/install/arm --with-python=no --without-zlib --without-lzma
make install

之后进入install/arm目录下,我们可以看到自动生成了bin、lib和include的文件夹。如果上述命令中,不设置--prefix的路径,那么会默认安装到/usr/local/路径下面。

 

进入install/arm6/bin目录下,使用file命令查看生成的bin文件,可以看到文件属性均为arm的版本。

相比x86版本的编译命令,编译arm版本的libxml多设置了CC和host,其余命令全部相同。因此,编译x86和arm平台下的libxml文件是通过configure命令设置CC和HOST实现的,一个用于指令编译器,一个用于指令运行的平台

 

总结

至此,关于交叉编译的基本内容讲完了。对于交叉编译的编译器、链接器等工具,我只知道由芯片供应商来提供,上层开发人员只需要调用交叉编译脚本,完成上层业务代码的编译即可。交叉编译常用于嵌入式开发,汽车行业的程序员还是需要了解简单的交叉编译知识,对参与的项目胸有成竹,而不要只埋头写代码。


文章首发于上汽零束开发者论坛。

作者:程序猿司晨
文章来源:上汽零束SOA开发者论坛 
原文链接:
https://bbs.z-onesoft.com/omp/community/front/api/page/mainTz?articleId=7644

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值