《基于GCC的嵌入式程序插装技术.docx》由会员分享,可在线阅读,更多相关《基于GCC的嵌入式程序插装技术.docx(8页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、基于GCC的嵌入式程序插装技术mahaiyan导语:本文介绍一种通过分析和修改GCC编译工具,实现程序插装的新方法。该方法具有批量自动插装,插装与编译连接严密结合,适用语言广泛等优点。最后详细讨论了怎样在ARM嵌入式程序中实现程序插装。摘要软件测试中,覆盖、故障注入、性能分析等广泛使用的动态测试方法均基于程序插装技术。本文介绍一种通过分析和修改GCC编译工具,实现程序插装的新方法。该方法具有批量自动插装,插装与编译连接严密结合,适用语言广泛等优点。最后详细讨论了怎样在ARM嵌入式程序中实现程序插装,并给出修改GCC的源代码。关键词GCC程序插装ARM嵌入式程序程序插装ProgramInstru
2、mentation概念最先是由JGHuang教授提出,是借助往被测程序中插入操纵称为“探针,以便获取程序的控制流和数据流信息,进而实现测试目的的方法。在软件动态测试中,程序插装是一种根本的测试手段,应用广泛,是覆盖率测试、软件故障注入和动态性能分析的根底技术。GCCGNUCompilerCollection是一个高度优化,高度可移植,广泛使用的编译系统。它能处理多种语言,包括CC+、Fortran、Java和Pascal等多种语言前端,而且后端支持几乎所有的处理器构造。GCC作为源码开放的软件,人们可以自由修改和使用;参加插装模块后,在GCC所支持的语言中都可插入相应的测试代码这里只介绍C语言
3、的插装模块。本文将具体表达怎样修改GCC,使其在编译每个C函数时,分别将各个形式参数连同该函数名传递给一个指定函数。该指定函数的返回值赋予原来的形式参数,进而可以人为控制被插装函数的每个参数实际值,进而完成各种规那么下的测试。1GCC编译流程分析编译器的工作是将源代码通常使用高级语言编写翻译成目的代码通常是低级的目的代码或机器语言。在当代编译器的实现中,这个工作一般是分为两个阶段来实现的:第一阶段,编译器的前端接收输入的源代码,经过词法、语法和语义分析等得到源程序的某种中间表示方式。第二阶段,编译器的后端将前端处理生成的中间表示方式进展一些优化,并最终生成在目的机器上可运行的代码。GCC编译器
4、以一个函数为单位对经过预处理的输入源文件进展编译处理。根据GNUBison一个类似YACC但功能更强大的文法分析工具生成的语法分析程序,前端完成语法、语义分析,建立语法树,并转换成中间代码。GCC内部使用了一种能对实际的体系构造做一种抽象的,与硬件平台无关的语言,这个中间语言就是RTLRegisterTtansferLanguage。通过修改源程序的RTL,可以改变、删除源程序,包括插入所需要的代码,由GCC后端处理并最终输出对应硬件平台的汇编码,源程序无需手工修改便可实现插装功能。GCC的入口点main函数在文件mainc中。此函数非常简单,只有一条直接调用toplev_main函数的语句。
5、toplev_main函数是在toplevc文件中定义的,以下我们只关心与编译有关的源码,其他的暂时忽略。toplev_main中最重要的是调用了do_complile函数,这个函数从名字看就是做编译工作的;而在此之后,toplev_main函数就返回了。dD_compile函数也是在tokvc中定义的,其中真正进展编译工作的是调用compilte_file函数。compik_file函数最终调用了一个钩子函数来分析parse整个输入文件:lang_hooksparse_fileset_yydebug;这里的lang_hooks是一个全局变量,不同语言的前端对此赋以不同的值。对C语言来讲,这条
6、语句相当于调用了c-optsc中的c_common_parse_file函数。c_com-mon_parse_file中调用了c-parsec中的c_parse_file函数;在此函数中又调用了同文件中的yyparse函数,该函数负责解析C语言源文件,并转化为特殊的语法树构造。该函数是GNUbison将YACC转变为C语言而自动生成的,所以这段代码浏览起来比拟困难,但我们并不关心语法分析的细节。在完成函数体的分析后,利用已经建立的tree构造生成RTL,优化后最终输出汇编码;自此C函数的编译就算完毕了,这些是由yyparse调用finish_function函数完成的。finish_funct
7、ion函数中最重要的函数是tree_rest_of_compilation定义在tree_optimize.c中,它是真正实现上述功能的函数。为了讲明它所做的详细事情,我们将该函数做了删减,保存了关键的地方。将函数各个局部展开成RTL形式后,调用函数rest_of_compilation将RTL输出为汇编码。至此,得到了一张明晰的GCC编译时的函数调用道路,如表1所列。2基于GCC的程序插装技术根据插装测试的要求,需要在函数开场时为每个参数调用钩子函数,并用钩子函数的返回值更新参数的值;同时,将被插装函数的名称压入函数本地栈内,作为该函数的一个匿名本地变量,只用于传递给钩子函数。从上面列出的t
8、ree_rest_of_compilation函数源码得知,负责建立被编译函数参数和返回值的函数是expand_function_start,定义是在文件functionc中。expand_function_start中处理函数参数和返回值的函数是assign_parms,这是需要十分关注的函数。以下是该函数简化的伪码:斜体加粗的局部是增加的代码。在for循环前,获得当前编译的函数名见源码中位置;但暂时不能输出到函数的RTL链中,由于本地栈要在所有参数传递完毕才完全建立起来。在for循环体完毕前,记录下函数参数的一份拷贝见,最后调用。insert_function_name_local函数,将
9、当前函数名插入本地栈,并且修正栈指针见。经过以上修改,得到了插装所需的所有信息,包括函数参数和函数名称的RTX表示。GCC将函数编译后生成的RTX表示以链表形式组织,最后一次性把这个RTX链表输出为后端平台的汇编码。完成这项工作的是rest_of_compilation函数,所以在调用rest_of_complilation函数前插入我们的RTX,最终完成插装,由函数inject_rtl负责完成。下面是inject_rtl的主要代码:3APCS与程序插装实现编译器必须以一套统一的方法编译函数的定义和调用经过,才能确保不同语言编写的函数能互相调用。规定这些细节的便叫作“函数调用标准Procedu
10、reCallStand-ard。ARM体系构造定义了自己的函数调用标准ARM函数调用标准ARMProcedureCallStandard,APCS。固然APCS不是强迫性的,但实现APCS并不困难,而且可获得统一的二进制兼容的好处,所以大局部的编译器都实现了APCS,其中包括GCC。APCS中函数传递参数的定义如下:前4个整数实参或更少被装载到r0r3。前4个整数实参或更少被装载到r0r3。前4个浮点实参或更少被装载到f0f3。假如参数为双字8字节,就必须从偶数存放器开场放置。假如一个参数不能完全放入存放器中,那么超过的那局部拷贝到栈中。其他任何实参假如有的话存储在内存中,用进入函数时紧接在s
11、p值上面的字来指向。换句话讲,其余的参数被压入栈顶。所以,要想简单,最好定义承受4个或者更少的整数参数的函数。本文所述的插入函数只有两个整型形参,所以调用时只需将两个实参分外传入ro和rl。GCC提供emit_li-brary_call函数用来生成函数调用的RTL码,GCC将按照APCS产生正确的函数调用汇编码。函数定义在callsc中,原型为:插入所需函数后,需要将返回值赋值给对应的被插装函数的形参。以下是插入函数insert_parms_test_function的完好代码:4实例为便于检查插装效果,用经过修改的GCC编译一段简单的C语言程序。该程序为一个独立函数foo,承受两个整数类型的
12、参数。详细代码如下:从GCC输出的汇编码可以看到,foo函数的两个参数都经过钩子函数pt_hook_partns的处理更新;在pt_hook_parms函数内,可以根据测试算法返回不同的边界值,进而到达测试的目的。按照此方法,一个实际程序经过插装后,在ARM模拟器上顺利运行,并获得预期的测试效果。本文具体地阐述了修改GCC增加插装功能的实现方法。按照这样的思路,成功地实现了基于ARM7芯片的嵌入式系统的动态参数边界测试,到达了预期的效果。本文所述的插装函数比拟简单,没有区分参数的类型,所有参数均按照一个字大小来处理;下一步的工作是细分参数不同类型,插装不同的处理函数。作为一种通用的插装方法,在此摹础上通过识别不同的插装点和插装不同的函数,可以实现函数调用栈检查,程序覆盖率测试,获取函数实际执行时间等需要插装技术作为根底的功能。