郝玉涛 孙建祥 安占新
1.中国载人航天工程办公室,北京 100071 2.北京航天自动控制研究所,北京 100854
2017年俄罗斯研制的 “联盟-2.1B”火箭在东方港基地发射,由于飞行软件使用的发射场坐标参数是拜科努尔发射场的数据,导致火箭未能进入预定轨道,坠落大西洋[1]。由此可见,飞行软件正确与否关系到运载火箭发射任务的成败,确保飞行软件执行程序的正确性是航天型号软件设计人员致力解决的重要问题之一。
目前,航天控制领域广泛使用德州仪器公司(Texas Instruments,简称TI) DSP(Digital Signal Processors)处理器进行控制计算。软件设计人员通过集成开发环境CCS(Code Composer Studio)或者CC(Code Composer)生成COFF(Common Object File Format,通用对象文件格式)格式的飞行软件,再通过其配套的转换工具,生成自举表(Boot Table)格式的文件,固化在计算机的FLASH中。计算机上电后,通过自带的bootloader引导程序,自动加载自举表格式的文件运行[2]。自举表格式的执行程序能够满足运载型号部分计算机上电自动引导的需求,但不能满足型号部分计算机需要通过二次引导程序[3-4]运行的需求。自举表格式的文件不仅包含DSP执行程序,还带有其它额外信息。二次引导的目标文件(DSP执行程序)不允许带有这些额外信息。
为了满足通过二次引导程序运行的需求,需要生成二进制格式的DSP执行程序。以往,软件设计人员主要采用2种人工提取方法:(1)通过集成开发环境控制仿真器,将COFF格式的目标文件下载到DSP处理器中,再按照MAP文件(用于表示程序、数据以及IO空间的映射)中的代码段以及初始化数据段地址,提取相应内存中的执行程序以及初始化数据;(2)软件设计人员人工分析COFF格式的文件,在文件中提取出代码段以及初始化数据段。这两种人工生成执行程序的方法使用设备多,环境复杂,操作繁琐,易出错,可靠性低。
文献[2]研究了DSP处理器引导功能的软硬件配置设计以及引导控制程序;文献[3]研究了基于二次引导程序的在线升级程序的方法;文献[4]研究了多核DSP的加载方法和流程;文献[5]研究了COFF文件向自举表文件的转换方法以及基于FLASH的自动加载方法;文献[6]研究了基于外部扩展FLASH存储器的程序代码自举加载方法。目前针对二次引导的目标文件如何可靠提取,没有相关文献进行论述。本文提出了基于COFF和自举表(Boot Table)格式文件的DSP执行程序提取器设计方案及实现技术,不仅解决了文献[2-6]中二次引导的目标文件自动提取问题,提高了生成效率和正确率,而且通过加壳技术[7],在目标文件中增加了DSP执行程序的CRC校验信息,提高了飞行程序使用的可靠性。
本文研究了COFF文件、自举表文件的格式,提出了基于上述两种文件提取DSP执行程序的算法,介绍了软件实现技术、功能验证、性能评估以及在航天重大工程中的应用情况。
通用对象文件格式COFF是一种很流行的对象文件格式,是程序源代码通过集成开发环境进行编译、链接之后,最终产生的一种模块化的文件格式。这种文件格式引入了“段”的机制,不同的目标文件可以拥有不同数量以及不同类型的段,为软件开发人员提供了一组二进制接口定义,这些接口可以延伸到多种操作环境,从而减少重新编码、重新编译程序的需要。
COFF文件包括文件头、可选文件头、段头信息表、段数据、重定位信息、行号入口表、符号表、字符串表[5]。其中段头信息表、段数据、重定位信息、行号入口表对应多个段;文件头、可选文件头、段头信息表、段数据与生成执行程序密切相关。
文件头用来保存COFF文件的基本信息,如段头的数目、时间戳、符号表位置、属性标记等,共占用22字节,其中第2、3字节指明了段头的数目。
可选头在文件头后面,从COFF文件的0x16偏移处开始,长度为28个字节,用来保存在文件头中没有描述到的信息,如执行代码的大小、初始化数据的大小、未初始化数据的大小、程序入口地址、执行代码的开始地址、初始化数据的开始地址等。
从第51字节开始,为各段段头信息表,用来描述段信息,每个段都有一个段头信息表来描述,段的数目在文件头中指出。每个段头信息表共48个字节,内容包括段名、段数据载入内存时的物理地址、段数据载入内存时的虚拟地址、段数据的实际长度、段数据在COFF文件中的偏移量、段属性。第9~12字节为此段在内存中的物理地址;第21~24字节为此段程序或者数据在COFF文件中的偏移地址;第41~44字节为段属性。段属性为0x00000020时,表示此段是执行代码段;为0x00000040时,表示此段是初始化数据段;为0x00000080时,表示此段是未初始化的数据段。
段数据用来保存各个段的数据,不同类型的段,其数据的内容和结构也不相同。在目标文件中,这些数据都是原始数据,无特别的格式。
在DSP系统中通常使用FLASH存储器保存程序,在上电或者复位时,BootLoader引导程序将存储在FLASH中的程序搬移到DSP片内或者片外RAM中,并跳转到用户程序入口运行,这个程序搬移过程叫自举加载。用户程序与一些必要的引导信息结合在一起,形成特定格式的自举表,以便BootLoader引导程序在自举加载过程中识别有效的用户程序、搬移地址以及程序入口地址。
自举表可以通过集成开发环境提供的工具生成,其过程为:(1)使用hex.exe工具,将集成开发环境生成的COFF格式文件转换成为工具能够识别的hex文件格式,转换前需要配置好程序引导地址、程序入口地址;(2)使用hexbin.exe工具,将hex格式文件转换为自举表格式文件。
自举表格式[6]文件包括内存宽度、STRB控制寄存器数值、数据块,其中数据块可以有多个,每个数据块包括数据块的大小、数据块加载的起始地址以及数据。文件最后一个字数值为0,表示文件结束。
COFF文件解析器首先以二进制形式读入扩展名为.out的COFF文件,分析COFF文件头,其第2、3字节指明了段的数目;根据段数目,逐段分析段头信息表,获取每段的属性标识,如果该段属性为执行代码或者初始化数据,记录执行代码段、初始化数据段内存中的物理地址、COFF文件中的偏移地址以及大小;计算执行程序(包含初始化数据)文件大小;计算执行代码段、初始化数据段在执行程序文件中的位置,按照线性映射关系将此段数据存储在相应内存中。COFF文件解析算法工作流程如图1所示。
图1 COFF文件解析算法工作流程
详细过程如下:
1)采用WIN32提供的内存映射文件机制[8],根据可选头中执行代码、初始化数据段的大小,在内存中申请COFF文件相应大小的地址空间区域,将COFF文件中的数据以二进制形式读入对应的地址空间区域。解析过程中直接读取内存中相应地址获取COFF 文件数据,不再对文件进行IO操作。
2)针对内存中的COFF文件数据,分析其文件头,确定段数目:N。
3)逐段分析段头信息表,如果该段属性为0x00000020时,记录此段内存中的物理地址MemAddrText_i、大小LenText_i、文件中的偏移地址FileAddrText_i(i=1,2,3,……);如果该段属性为0x00000040时,记录此段内存中的物理地址MemAddrInitData_j、大小LenInitData_j、文件中的偏移地址FileAddrInitData_j(j=1,2,3,…….)。
4)计算执行程序文件大小LenExeFile,并申请相应大小的地址空间区域,用来与执行程序、数据文件进行线性映射:
AddrLow=Min(FileAddrText_i,MemAddrInitData_j)(i=1,2,3,……,j=1,2,3,…,Min表示求最小值)。
AddrHigh=Max(FileAddrText_i,MemAddrInitData_j)(i=1,2,3,……,j=1,2,3,…,Max表示求最大值)。
LenExeFile=AddrHigh-AddrLow+LenLast(LenLast为通过Max获得的起始地址最大的段的长度)。
5) 逐段分析段头信息表,如果该段属性为执行代码或者初始化数据,计算此段数据在执行程序文件中的位置,按照线性映射关系将此段数据存储在相应内存中:MemAddrSec_k(k=1,2,3,……)为执行代码段或者初始化数据段内存中的物理地址, 执行代码段或者初始化数据段在执行程序文件中的位置:
ShiftAddrSec_k=MemAddrSec_k-AddrLow(k=1,2,3,……)。
6)完成所有段的分析之后,按照线性映射关系将内存中的数据存储在文件中,获得执行程序文件。
自举表文件解析器首先以二进制形式读入自举表文件,逐一分析各数据块,获取各数据块的大小,据此计算执行程序文件的大小,并申请相应内存空间;根据各数据块的大小、数据块加载的起始地址,计算此段数据在执行程序文件中的位置,并将数据块的数据按照计算的位置存储在内存空间;将内存空间中的数据最终存储在执行程序文件中。自举表文件解析算法工作流程如图2所示。
图2 自举表文件解析算法工作流程
详细过程如下:
1) 读入自举表文件,获取各段数据的大小LenSec_i(i=1,2,3,……),计算执行程序文件映射内存大小∑LenSec_i(i=1,2,3,……),申请相应空间,并清零。
2)重新读取自举表文件,跳过内存宽度和寄存器数值,指向第一个数据块的信息头,针对每一数据块逐一进行3)~6)的处理。
3)判断自举表文件当前内容是否为0,为0,表示自举文件结束,否则,表示仍有数据块需要处理。
4)依次获取本段数据块的大小以及起始地址。
5)计算本段数据在执行程序文件映射内存中的相应位置,并将数据存储在相应位置的内存中。
6)如果自举表文件结束,则将内存中的数据存储在执行程序文件中。
自举表文件解析算法也采用WIN32提供的内存映射文件机制,其操作过程和方法与COFF文件解析算法相同。
本文以TI公司DSP为例进行说明,将集成开发环境CCS或者CC编译、链接生成的COFF文件作为输入文件,经过COFF文件解析器解析,生成执行程序;使用集成开发环境提供的hex.exe工具以及hexbin.exe工具,由COFF文件生成自举表格式文件;自举表格式文件经过其解析器解析,生成执行程序;为了提高生成执行程序的可靠性,对由COFF格式和自举表格式文件生成的执行程序,通过二进制比较器进行逐字节比对,两套执行程序大小、内容完全一致,则认为提取正确;最后通过二进制文件编辑器,使用加壳技术[7],在执行程序头部增加长度和CRC校验信息,生成带CRC校验信息的执行程序文件。软件实现过程如图3所。
图3 软件实现过程
在使用带CRC校验信息的执行程序文件前,可以根据长度、CRC校验码确认文件的完整性和正确性;使用时,通过脱壳技术[9],删除CRC校验信息和文件长度。带CRC校验信息的执行程序文件结构如图4所示。
图4 带CRC校验信息的执行程序文件结构
0~3字节,存放执行程序的长度(以字节为单位);4~5字节,填充0;6~7字节,存放执行程序的16位CRC校验码,计算CRC校验码的多项式为X16+X15+X2+1;从第8个字节开始,依次存放执行程序。
COFF目标文件包含text段、data段、bss段、const段、cinit段、switch段、stack段、system段、far段、sect指令定义的初始化段、usect指令定义的未初始化段等。这些段的含义如表1所示。其中text段、const段、cinit段、switch段以及sect指令定义的段为初始化段,需要提取到执行程序文件。
表1 COFF文件段的组成
在对提取器进行验证时,测试用例考略了COFF文件的完整性、应用程序工程中链接文件定义段的随机性、提取器软件防错处理等情况,共考虑10项内容、117个组合情况,设计用例519个。详细情况如表2所示。
表2 提取器测试内容概况
针对使用提取器生成执行程序(以下简称方式一)、使用集成开发环境通过仿真器提取执行程序(以下简称方式二)、人工分析COFF文件提取执行程序,设计人员进行了生成正确率和性能对比分析。
在载人航天工程、探月工程、探火工程的新一代运载火箭型号的分系统综合试验、匹配试验、出厂测试、靶场测试以及发射任务过程中,随机选择了80KByte~300KByte大小的执行程序、23位设计人员,初始化段和未初始化段随机分配,每种状态分别使用3种方式生成,每种方式均进行了200次,一次正确率(以往人工生成时,通过多次生成避免生成错误)测试结果如表3所示。
表3 一次正确率测试结果
上述测试过程中,记录三种方式每次均提取正确的时间,随机选择100次的测试结果进行分析,如表4所示。
表4 性能测试结果
性能评估测试结果表明,通过提取器生成执行程序,不仅能够确保100%正确生成,提高了生成正确率,而且将生成效率提高了约20倍。
以往人工生成DSP执行程序的方法使用设备多,环境复杂,操作繁琐,易出错,可靠性低。本文提出了基于COFF和自举表文件的DSP执行程序提取器设计及实现技术,并在载人航天工程、探月工程、探火工程的新一代运载火箭型号中进行了广泛应用。通过功能验证、性能评估以及型号应用实践,结果表明,使用提取器,能够快速、自动生成带有CRC校验信息的DSP执行程序,该技术极大提高了DSP执行程序的生成效率、正确率、可靠性和使用安全性。