1. 研究背景
1.1零插桩目标码覆盖率统计技术
随着武器装备的复杂度的大幅度增加,运行在装备上的嵌入式系统也越来越复杂,功能迭代越来越多,代码中就可能就会存在部分无用代码,或者在执行过程中无法测试覆盖的分支,这可能就会给软件带来很大的漏洞,严重降低软件的可靠性。因此,需要构建一个能够动态分析代码执行覆盖情况的能力,来检测代码中未执行覆盖情况。
通过覆盖率统计分析,得出嵌入式软件执行覆盖情况,查看软件中有那些函数未执行,函数中哪些分支未执行,由此分析软件中函数为什么没有执行,函数中分支为什么没有执行,调整测试方法使尽可能多的函数和函数分支得到测试,大大提升软件的可靠性。
在覆盖率分析时需要分析覆盖率、指令函数覆盖率、指令函数分支覆盖率、源代码行覆盖率、源代码函数覆盖率、源代码函数分支覆盖率。
目前大部分覆盖率统计技术都是通过在源代码中插桩的方式来进行统计覆盖率情况,就是在函数入口,分支语句等地方写入特定标志语句,执行后统计标志信息,来计算覆盖率情况,但是这样做会带来很多的额外成本,如:需要修改源代码,目标码文件增大等问题。这就增加了人工成本,或者由于目标码过大无法下载到目标板等等问题。为解决这些问题,我们需要实现一个不需要插桩就能统计覆盖率的技术。
2. 关键技术
2.1零插桩目标码覆盖率统计技术
(1)零插桩技术
零插桩统计覆盖率需要分析的是嵌入式软件的基本特性和执行特性。在不插桩的前提下,使用反汇编工具生成嵌入式软件目标码的反汇编文件,通过分析反汇编文件获取想要的基础信息,如函数指令信息,地址对应源码行号信息等。在与仿真验证工具配合获取执行PC地址信息,分析嵌入式软件的执行覆盖情况,实现零插桩分析覆盖率的目的。
(2)多架构嵌入式软件目标码分析技术
针对不同架构,如:ARM、SPARC、DSP、PPC等构建相应的嵌入式软件目标码分析技术,能够准确分析嵌入式软件目标码的反汇编文件中的函数名称、函数所有的指令地址信息、函数中分支指令的信息,分支指令的PC地址,分支指令的跳转地址,分支指令的下一条地址等信息、PC指令地址所对应源码的行号信息等。通过这些基础信息,与执行信息配合实现统计覆盖率信息。
(3)数据缓存技术
反汇编和数据分析都是一个比较耗时的阶段,对于大型嵌入式软件可能会有几十万甚至上百万的指令数量级,每次进行分析都会耗费一定的时间,为减少时间上的浪费,提出数据缓存技术,在执行的目标码不变的情况下,尽可能的存储下所有固定分析数据,如目标码的反汇编文件,反汇编文件分析结果,源码文件分析结果等。都可以存储下来,但是在目标码文件修改后,所有缓存文件都需要进行相应更新。
3. 技术途径
3.1零插桩目标码覆盖率统计技术
(1) 零插桩技术
零插桩技术主要需要分析嵌入式软件的目标码及源代码,通过提取目标码及源代码中的特征信息,获取函数信息,及函数中的分支信息。整体流程如下:
(2)动态执行PC信息
在仿真环境中,运行嵌入式软件就是将嵌入式软件编译好的二进制文件解析成对应的汇编指令写入仿真硬件环境中,然后一条条执行指令,模拟出嵌入式软件在真实环境中的运行效果。在运行过程中将执行的每条指令地址记录下来就可以得到执行信息。
记录指令执行信息通过创建一个记录设备来完成记录工作,每次处理器执行一条指令时将地址信息传给记录设备由记录设备存储执行地址信息。在记录设备中,会根据收到的地址信息创建相应的地址表来记录执行情况,当收到地址信息后检索当前地址表是否有该地址如果没有将创建一块地址空间映射该地址,如果有该地址的地址表,将会在该地址表对应位置做+1操作。在需要执行信息的时候将检索所有的地址表,将所有非0的地址输出到文件中,达到记录指令流的效果。
(3)目标码文件分析
在反汇编文件中会展示目标码中所有的指令信息,包括指令地址、指令操作数、指令名称等信息,通过处理该反汇编文件就可以获取统计目标码覆盖率所需的信息。
经过调研发现反汇编文件的格式都是固定的,都基本满足如下格式:
解析反汇编文件获取所有的函数指令地址信息。解析过程中还需要获取跳转指令信息,跳转信息获取需要获取三个地址,跳转指令的地址,来确定这个跳转指令是否被执行,跳转的地址,来确定是否进入True分支,跳转指令的下一条地址,来确定是否执行False分支。
每种架构的指令格式都不尽相同所以需要单独处理。
(4)源代码文件分析
在反汇编时,加入-l参数可以在反汇编信息中加入源代码行号信息,如下图:
经调研发现反汇编信息中,行号信息与指令信息对应关系是固定的,都基本满足如下格式:
处理反汇编文件信息,提取文件行号与指令地址对应信息,计算时如行号的某一条指令被执行那么对应源代码的这一行也就被执行了,但是在反汇编信息中只有产生指令操作的源代码才被认为是有效行,所以本项目中也只统计产生指令操作的有效行,进行各种计算。
源代码的分支信息需要通过分析源代码文件获得准确的分支信息,本项目中采用clang工具提取源代码文件的抽象语法树,通过抽象语法树获取源代码的分支信息。获取到抽象语法树后提取语法数中关于for、if...else、while、switch...case...default等代码块的行号。
其中for、while的分支计算方法为如果执行了该分支起始行不管有没有执行分支块内代码也会认为执行该分支的False分支,如果执行了分支块内代码那也就会认为执行了该分支的True分支。
其中if分支有多种情况如下:
情况一:
if(xxxxxx){xxxxxxx
}
情况二:
if(xxxxxx){xxxxxxx
}else{xxxxxxx
}
情况三:
if(xxxxxx){xxxxxxx
}else if{xxxxxxx
}else{xxxxxxx
}
在计算时遇到情况一,如果执行到了分支起始行不管有没有执行分支块内代码也会认为执行该分支的False分支,如果执行了分支块内代码那也就会也会任务执行了该分支的True分支。遇到了情况二,如果执行了true分支内代码行则认为执行了true分支,如果执行了false分支内代码行则认为执行了false分支。遇到情况三,会将代码分成两个分支对,第一个分支对:if下代码块为true分支,else if下代码块为flase分支,第二个分支对:else if下代码块为true分支,else下代码块为false分支,如果有更多的else if 分析方法一致。
其中switch...case...default的计算方法为有多少个case、default就有多少个分支。
(5)结果计算
获取所需信息后,就可以通过公式计算所需覆盖率结果。
目标码总语句覆盖率:
目标码总分支覆盖率:
目标码函数语句覆盖率:
目标码单个函数分支覆盖率:
源代码总语句覆盖率:
源代码总分支覆盖率:
源代码函数语句覆盖率:
源代码单个函数分支覆盖率:
3.2 多架构嵌入式软件目标码分析技术
每种架构的嵌入式软件目标码反汇编格式整体结构基本一致,但不同架构的指令格式都不尽相同,都有各自的特点,如果只需要函数指令PC地址信息则不需要继续分析,但是我们还需要统计函数中的分支覆盖率情况。每种架构的分支指令不同,格式上也不尽相同,针对不同架构都需要构建对应的指令分析方式。
不同架构的跳转指令如下表:
架构 | 跳转指令 |
SPARC | ba,bcc,bcs,be,bg,bge,bgu,ble,bleu,bn,bne,bneg,bpos,bbc,bvs,fble,fbne,fbule,fbuge |
MIPS | bnez,beq,bne,beqz,bteqz,btnez |
TIC6x | bnop,b |
TIC28x | sb,sbf |
TIC55x | BCC |
POWERPC | bgt,bne,ble,beq,bge,blt |
ARM | beq,bgt,bls,bhi,bcc,bcs,ble,bne,bge,blt |
通过调研总结发现如下规律:
SPARC架构跳转指令格式:
be 40101b9c <_longjmp+0x54>
be,a 40101ba8 <_longjmp+0x60>
MIPS架构跳转指令格式:
bnez v0,bf001990 <myprintf+0xa68>
beq a0,a3,bf0018c8 <myprintf+0x9a0>
TIC6x架构跳转指令格式:
[b0] b .S1 10800a28 <$C$L4>
[!b0] b .S1 10802220 <__c6xabi_llshl>
|| [!b0] b .S1 10802220 <__c6xabi_llshl>
TIC28x架构跳转指令格式:
sb 13, geq
sb fffffff2, neq
PPC架构跳转指令格式:
blt- 10550c <ipnet_configure+0x1f8>
beq- cr7,1055b0 <ipnet_string+0x68>
ARM架构跳转指令格式:
beq 12a68 <strlen+0x24>
bcs 12b64 <__default_signal_handler+0x8c>
TIC55x架构跳转指令格式:
BCC __cmpd,AC2>=#0
BCC #0x0024b0,!TC1
根据以上指令格式特点均可提取出想要的指令信息。
3.3 数据缓存技术
在仿真验证工具中运行的目标码修改都是通过修改源代码实现的,所以在做数据缓存时主要针对这两个地方进行监控,如果目标码文件或者源代码文件进行了修改那么就要更新相应的数据文件,如目标码文件修改就要修改反汇编文件,反汇编文件解析数据,如源码文件修改就要修改源码文件所对应的函数起止行号信息、分支行号信息等。
在记录时以记录每个文件的md5值作为标准,如果目标码文件或者源码文件的md5值与记录中的不匹配那么就证明文件做了修改,如果中间分析文件的md5值与记录中的也不匹配那么就证明中间文件被篡改,都需要重新生成相应的文件,以保证结果的正确性。