陈 鹏,陈丽容,高艳鹍,李洪波
(1.中国航天科工集团第二研究院 北京计算机技术及应用研究所,北京 100854;2.73658部队 信息技术中心,北京 239400)
随着我国操作系统技术的迅猛发展,对其进行兼容性测试一直是一个亟需解决的问题[1]。目前对操作系统进行兼容性测试主要是判断其实现是否符合国际标准LSB[2]或者POSIX[3]。然而目前国产操作系统厂商众多,如中标、普华、麒麟等操作系统都存在着大量的用户群体,并且操作系统厂商都有着各自的开发标准并且各开发标准存在着较大的差异,导致国产操作系统难以形成统一的开发标准;另外操作系统开发厂商经常各自适配不同的第三方库,导致标准符合性的测试方法无法解决当前的全部需求[4]。许侃等曾提出通过编写测试脚本在整机上运行来测试操作系统整机兼容性的方法,测试了硬件在操作系统上的运行性能以及稳定性[5]。在其它操作系统领域,任海鹏等曾提出利用安卓原生CTS测试包以及人机交互界面的Android操作系统兼容性测试技术,提升了兼容性测试的自动化能力[6]。另外,张晓敏提出针对软件功能设计测试环境及用例,测试软件在操作系统中运行效率的测试方法[7]。本文吸取前人将操作系统与软硬件结合测试的经验,从操作系统兼容性概念着手,对应用程序在操作系统中运行的原理进行分析,提出了一种基于依赖要素比对的国产操作系统兼容性测试方法,并利用相关实验验证了该方法的可行性。
本文对操作系统兼容性的概念进行分析,了解到操作系统的兼容性测试可以引申到应用程序在操作系统中的运行情况,进而对应用程序在操作系统中运行的原理进行解析,了解到应用程序在操作系统中运行需要对操作系统提供的库函数进行调用,而对库函数的调用又依赖于操作系统提供准确的库函数签名。进而明确了通过比对库函数得到操作系统兼容性测试结果的方法。
通过对ELF文件格式进行分析,明确操作系统内可执行程序以及库文件都是以ELF文件格式保存的,并且ELF文件格式中保存了库文件的各种信息以及库文件所包含的库函数的信息,进而找到了基于对ELF文件格式进行分析得到操作系统兼容性依赖要素的方法。
最后通过上述两点的分析,明确了操作系统兼容性依赖要素并对依赖要素进行了分级。
另外,由源代码到可执行程序的过程需要经过编译器、连接器等工具的配合工作才能实现,考虑到编译器等工具并非操作系统提供的工具,为不可控因素,故在本文提供的兼容性测试方法中默认库函数的编译由同一类编译器、连接器进行,进而排除编译器等对可执行代码的影响。
操作系统兼容性是指在一个操作系统上面开发的应用程序可以不加修改、不用编译直接运行于另外一个操作系统上的特性[8]。我们把前者称为当前操作系统,把后者称为目标操作系统。程序在操作系统中转变为可执行文件需要经历4个步骤:预编译、编译、汇编以及链接。前3个步骤是对程序本身进行分析,将程序翻译为机器可以执行的指令的过程;链接是一个重定位的过程,当指令内涉及某个符号时,需要从操作系统或者程序本身找到这些符号,将目标在系统中的地址填入指令中,这个过程就是链接。链接分为静态链接与动态链接两种,其中静态链接是将多个文件组合成一个可执行程序的过程,动态链接则是对系统库文件进行查询,找到程序调用的库函数的地址进行重定位的过程。通过以上分析可以知道,程序在操作系统上的运行涉及到对系统库函数的调用,因此本文找到当前操作系统以及目标操作系统的库文件和库函数,对两操作系统依赖要素进行比对,根据比对结果判断操作系统兼容性。
ELF(executable and linkable format)是UNIX系统实验室(USL)作为应用程序二进制接口(application binary interface,ABI)而开发和发布的,也是Linux的主要可执行文件格式[9]。它保存了程序编译以及执行时的各种相关信息(如文件名、文件绝对路径、版本号、代码段、数据段、.bss段等)。
ELF一个特别的优点在于同一文件格式可以用于Linux内核支持的几乎所有的体系结构上。该格式标准不仅用于用户空间应用程序和库文件,还用于构建模块,且内核本身也是ELF格式。ELF文件格式包括文件头(ELF header)、程序头(program header table)、节头表(section header table)以及各个段(sections)[10]。表1给出了ELF文件格式的一些字段及其含义。
可以看出ELF文件头中保存着各个段的起始地址以及长度,通过地址以及长度可以计算出各个段的地址范围;.text段保存着程序以及库文件的代码段,通过对.text段进行解析,就可以得到库文件包含的所有库函数的编译信息;.symtab段保存着库文件的符号表,其中包含符号的名称、地址以及长度,通过判断符号的地址是否在.text段的地址范围内,就可以确定该符号是否是库文件所包含的库函数[11]。
程序能在操作系统中运行主要依靠的是调用系统提供的库函数[12],对库函数的准确调用又依赖于系统提供正确的库函数签名,考虑到程序依赖的多个库函数可能存在于同一个库文件中,操作系统兼容性依赖要素主要分为3级,分别为库文件的.text段、库函数对应的.text段、库函数签名对应的.text段,这里的函数签名指函数的参数传递。
操作系统的升级往往伴随着库函数的修改,两个不同类型的操作系统也必然存在着库函数实现上的差异,为了保证用户程序可以在不加修改的情况下在目标操作系统中运行,应保证软件在当前和目标操作系统中调用的库函数的签名相同,基于依赖要素比对(dependency function element comparison,DFEC)的兼容性测试方法基于该原则,对当前和目标操作系统在库函数的实现层面进行比对,并根据比对结果将兼容性测试报告反馈给用户。
DFEC兼容性测试方法分为两个过程:首先对当前操作系统和目标操作系统进行库文件扫描,获取库文件和库函数的包含关系,搭建操作系统库函数模型;之后根据搭建好的库函数模型,对当前和目标操作系统进行依赖要素的分级比对,得到兼容性测试结果。
在操作系统中,库函数信息以二进制码的形式保存在库文件中,扫描库函数信息要先获取操作系统的所有库文件,而库文件又以.so的后缀名保存在操作系统中,因此首先遍历操作系统文件资源,获取所有带有.so后缀名的库文件。这里的库文件包括系统自带的库文件以及第三方库文件。
找到了库文件后,需要基于ELF文件格式对库文件进行扫描,获取库文件包含的库函数信息,基于1.2节对ELF文件格式的介绍,ELF文件格式的.symtab段保存了文件的符号表,其中包含了符号的名称、地址以及长度,为了保证提取的符号是库文件包含的库函数,需要对符号表进行筛选,ELF文件格式的函数信息保存在.text段中,另外在ELF的头部表中保存着各个段的地址以及长度,从中取出text的起始地址和长度,就可以得到.text段的整段范围,这样,对.symtab段中的符号地址进行范围筛选就能得到所有在.text段中的符号,也就是库函数。
在知道了库函数的名称、地址以及长度后,就可以从库文件中读出所有库函数的信息,进而对库函数信息进行解析,获取每个库函数对应的.text段内容以及库函数签名对应的.text段内容。扫描库函数信息的流程如图1所示。
图1 扫描库函数信息
从库文件ELF格式中获取的.text段内与各依赖要素有关的部分实际上是一串二进制数,这些二进制数对应机器指令的机器码,鉴于二进制数过于冗长,并且对依赖要素的比对只需判断两操作系统下的依赖要素是否相同,不需要知道依赖要素的具体改动情况,我们将获取的二进制数利用哈希算法转换成哈希值,对两操作系统下的依赖要素进行哈希值比对,节省比对的时间。
构建好库函数模型后,依据分级好的操作系统兼容性依赖要素,以库函数为单位对目标和当前操作系统进行依赖要素比对。比对之前需要确认在目标操作系统中是否可以找到当前操作系统提供的库文件,在找不到库文件的情况下,认为目标操作系统缺少需要的库文件,不能兼容于当前操作系统。
在找到所有的库文件后,按照依赖要素分级,分别对当前操作系统以及目标操作系统中每一个库文件.text段、库文件下库函数的.text段、库函数签名的.text段进行逐级比对,如果比对过程中有库文件的.text段一致,则认为该库文件下的所有库函数未进行修改,可以判定该库文件下的所有依赖要素未影响操作系统的兼容性;如果库文件的.text段不一致,则需要对该库文件下的所有库函数进行之后的两个.text段的比对,若比对过程中某一级的.text段一致,则认为库函数未进行对操作系统兼容性有影响的修改,判断该库函数的修改未影响操作系统兼容性。如果所有库函数的修改都被判定为未影响操作系统兼容性,则认为操作系统的修改没有影响到程序的运行,目标操作系统兼容于当前操作系统;若对某个库函数的比对过程中每一级依赖要素的比对结果都不一致,则认为该库函数的修改影响了程序的调用,对该库函数进行调用的应用程序无法在目标操作系统上正确运行,目标操作系统不能兼容于当前操作系统。具体工作流程如图2所示。
图2 依赖要素分级比对流程
从2.3节中可以得到当前操作系统和目标操作系统的依赖要素比对结果,从依赖要素分级比对流程图可以看出,依赖要素的比对结果决定着操作系统兼容性比对结果是兼容还是不兼容。对于比对结果是兼容的两个操作系统,证明在当前操作系统下运行的应用程序可以不加修改地迁移到目标操作系统中。对于比对结果是不兼容的操作系统,在当前操作系统下运行的部分应用程序对目标操作系统中导致两操作系统不兼容的库函数进行了调用,因此无法直接迁移到目标操作系统中,对这些应用程序,DFEC兼容性测试方法对两操作系统下比对结果不相同的依赖要素进行标红显示,为应用程序设计厂商以及操作系统厂商提供修改建议。
为了验证方法的可行性,分别对利用同一套编译工具的不同类型的操作系统和相同类型操作系统的不同版本的兼容性进行测试,并根据应用软件的实际运行情况验证测试结果的准确性。
3.1.1 测试方案
当前操作系统:centos7
目标操作系统:deepin15.4
基于DFEC兼容性测试方法,对两操作系统中包含的所有库文件进行扫描,获得centos7操作系统和deepin15.4操作系统的库函数依赖模型,对两个操作系统的依赖要素进行比对,根据比对结果给出centos7操作系统和deepin15.4操作系统的兼容性测试结果。
3.1.2 兼容性测试结果
对两操作系统下的库文件进行扫描,比对库文件中包含的库函数信息。部分比对结果见表2。
在目标操作系统中找到了部分当前操作系统的库文件,这些库文件包含的库函数在实现上与当前操作系统不同,但在函数签名部分的哈希值比对结果还是一致的,但由于有部分库文件以及库函数未在目标操作系统下找到,判定对这些库文件进行调用的程序无法被移植到目标操作系统下,目标操作系统不能兼容于当前操作系统。
3.1.3 应用软件运行情况
选取dot工具对兼容性测试结果进行验证,dot工具在centos下对libgvc.so.6和libcgraph.so.6进行了调用,这两个库文件在deepin15.4中没有被找到,因此dot工具应该不能在deepin15.4下运行。将dot工具移植到deepin下运行,系统显示“dot not found”,证明dot工具不能在deepin15.4操作系统下运行,兼容性测试结果无误。
表2 centos7和deepin15.4依赖要素比对结果
3.2.1 测试方案
当前操作系统:deepin15.4
目标操作系统:deepin15.9
基于DFEC兼容性测试方法,对两操作系统中包含的所有库文件进行扫描,获得deepin15.4操作系统和deepin15.9操作系统的库函数依赖模型,对两个操作系统的依赖要素进行比对,根据比对结果给出deepin15.4操作系统和deepin15.9操作系统的兼容性测试结果。
3.2.2 兼容性测试结果
对两操作系统下的库文件进行扫描,比对库文件中包含的库函数信息。部分比对结果见表3。
表3 deepin15.4和deepin15.9依赖要素比对结果
经过扫描和比对发现,deepin15.4下的5704个库文件在deepin15.9下均存在,并且库文件的哈希值比对结果均一致,证明库文件均未发生改变,因此判断目标操作系统兼容于当前操作系统。
3.2.3 应用软件运行情况
我们选取了包括Firefox浏览器、Google浏览器等常用软件,结果这些软件在两操作系统下都可以正常运行,证明了两个操作系统具备良好的兼容性,与实验结果一致。
本文针对目前国产操作系统缺少兼容性测试方法的现状,基于对操作系统兼容性概念以及动态链接原理的研究,提出了一种基于依赖要素比对的国产操作系统兼容性测试方法。该方法对操作系统内与兼容性有关的依赖要素进行提取和分级比对,根据比对结果得到国产操作系统兼容性测试结果,并利实验数据验证了方法的可行性。结果表明,基于依赖要素比对的国产操作系统兼容性测试方法不仅对操作系统兼容性给出了测试结果,还对导致操作系统不兼容的问题进行了定位,从而为国产Linux操作系统兼容性测试提供了研究方向和解决方案。