位置:51电子网 » 技术资料 » EDA/PLD

基于GCC的嵌入式程序插装技术

发布时间:2008/5/28 0:00:00 访问次数:1053

  本文介绍一种通过分析和修改gcc编译工具,实现程序插装的新方法。该方法具有批量自动插装,插装与编译连接紧密结合,适用语言广泛等优点。最后具体讨论了如何在arm嵌入式程序中实现程序插装,并给出修改gcc的源代码。

引 言

  程序插装(program instrumentation)概念最先是由j.g.huang教授提出,是借助往被测程序中插入操作(称为“探针”),以便获取程序的控制流和数据流信息,从而实现测试目的的方法。在软件动态测试中,程序插装是一种基本的测试手段,应用广泛,是覆盖率测试、软件故障注入和动态性能分析的基础技术。

  gcc(gnu compiler collection)是一个高度优化,高度可移植,广泛使用的编译系统。它能处理多种语言,包括c/c++、fortran、java和pascal等多种语言前端,而且后端支持几乎所有的处理器结构。gcc作为源码开放的软件,人们可以自由修改和使用;加入插装模块后,在gcc所支持的语言中都可插入相应的测试代码(这里只介绍c语言的插装模块)。本文将详细叙述如何修改gcc,使其在编译每个c函数时,分别将各个形式参数连同该函数名传递给一个指定函数。该指定函数的返回值赋予原来的形式参数,从而可以人为控制被插装函数的每个参数实际值,进而完成各种规则下的测试。

1 gcc编译流程分析

编译器的工作是将源代码(通常使用高级语言编写)翻译成目标代码(通常是低级的目标代码或者机器语言)。在现代编译器的实现中,这个工作一般是分为两个阶段来实现的:

第一阶段,编译器的前端接收输入的源代码,经过词法、语法和语义分析等得到源程序的某种中间表示方式。

第二阶段,编译器的后端将前端处理生成的中间表示方式进行一些优化,并最终生成在目标机器上可运行的代码。

gcc编译器以一个函数为单位对经过预处理的输入源文件进行编译处理。根据gnu bison(一个类似yacc但功能更强大的文法分析工具)生成的语法分析程序,前端完成语法、语义分析,建立语法树,并转换成中间代码。gcc内部使用了一种能对实际的体系结构做一种抽象的,与硬件平台无关的语言,这个中间语言就是rtl(register ttansfer language)。通过修改源程序的rtl,可以改变、删除源程序,包括插入所需要的代码,由gcc后端处理并最终输出对应硬件平台的汇编码,源程序无需手工修改便可实现插装功能。

gcc的入口点main函数在文件main.c中。此函数非常简单,只有一条直接调用toplev_main函数的语句。toplev_main函数是在toplev.c文件中定义的,以下我们只关心与编译有关的源码,其他的暂时忽略。toplev_main中最重要的是调用了do_complile函数,这个函数从名字看就是做编译工作的;而在此之后,toplev_main函数就返回了。dd_compile函数也是在tokv.c中定义的,其中真正进行编译工作的是调用compilte_file函数。compik_file函数最终调用了一个钩子函数来分析(parse)整个输入文件:

(*lang_hooks.parse_file)(set_yydebug);

这里的lang_hooks是一个全局变量,不同语言的前端对此赋以不同的值。对c语言来说,这条语句相当于调用了c-opts.c中的c_common_parse_file函数。c_com-mon_parse_file中调用了c-parse.c中的c_parse_file函数;在此函数中又调用了同文件中的yyparse函数,该函数负责解析c语言源文件,并转化为特殊的语法树结构。该函数是gnu bison将yacc转变为c语言而自动生成的,所以这段代码阅读起来比较困难,但我们并不关心语法分析的细节。在完成函数体的分析后,利用已经建立的tree结构生成rtl,优化后最终输出汇编码;自此c函数的编译就算结束了,这些是由yyparse调用finish_function函数完成的。finish_function函数中最重要的函数是tree_rest_of_compilation(定义在tree_optimize.c中),它是真正实现上述功能的函数。为了说明它所做的具体事情,我们将该函数做了删减,保留了关键的地方。

将函数各个部分展开成rtl形式后,调用函数rest_of_compilation将rtl输出为汇编码。至此,得到了一张清晰的gcc编译时的函数调用路线。

2 基于gcc的程序插装技术

根据插装测试的要求,需要在函数开始时为每个参数调用钩子函数,并用钩子函数的返回值更新参数的值;同时,将被插装函数的名称压入函数本地栈内,作为该函数的一个匿名本地变量,只用于传递给钩子函数。从上面列出的tree_rest_of_compilation函数源码得知,负责建立被编译函数参数和返回值的函数是expand_function_start,定义是在文件function.c中。expand_function_start中处理函数参数和返回值的函数是assign_parms,这是需要特别关注的函数。

斜体加粗的部分是增加的代码。在for循环前,获得当前编译的函数名(见源码中①位置);但暂时不能输出到函数的rtl链中,因为本地栈要在所有参数传递完毕才完全建立起来。在for循环体结束前,记录下函数参数的

  本文介绍一种通过分析和修改gcc编译工具,实现程序插装的新方法。该方法具有批量自动插装,插装与编译连接紧密结合,适用语言广泛等优点。最后具体讨论了如何在arm嵌入式程序中实现程序插装,并给出修改gcc的源代码。

引 言

  程序插装(program instrumentation)概念最先是由j.g.huang教授提出,是借助往被测程序中插入操作(称为“探针”),以便获取程序的控制流和数据流信息,从而实现测试目的的方法。在软件动态测试中,程序插装是一种基本的测试手段,应用广泛,是覆盖率测试、软件故障注入和动态性能分析的基础技术。

  gcc(gnu compiler collection)是一个高度优化,高度可移植,广泛使用的编译系统。它能处理多种语言,包括c/c++、fortran、java和pascal等多种语言前端,而且后端支持几乎所有的处理器结构。gcc作为源码开放的软件,人们可以自由修改和使用;加入插装模块后,在gcc所支持的语言中都可插入相应的测试代码(这里只介绍c语言的插装模块)。本文将详细叙述如何修改gcc,使其在编译每个c函数时,分别将各个形式参数连同该函数名传递给一个指定函数。该指定函数的返回值赋予原来的形式参数,从而可以人为控制被插装函数的每个参数实际值,进而完成各种规则下的测试。

1 gcc编译流程分析

编译器的工作是将源代码(通常使用高级语言编写)翻译成目标代码(通常是低级的目标代码或者机器语言)。在现代编译器的实现中,这个工作一般是分为两个阶段来实现的:

第一阶段,编译器的前端接收输入的源代码,经过词法、语法和语义分析等得到源程序的某种中间表示方式。

第二阶段,编译器的后端将前端处理生成的中间表示方式进行一些优化,并最终生成在目标机器上可运行的代码。

gcc编译器以一个函数为单位对经过预处理的输入源文件进行编译处理。根据gnu bison(一个类似yacc但功能更强大的文法分析工具)生成的语法分析程序,前端完成语法、语义分析,建立语法树,并转换成中间代码。gcc内部使用了一种能对实际的体系结构做一种抽象的,与硬件平台无关的语言,这个中间语言就是rtl(register ttansfer language)。通过修改源程序的rtl,可以改变、删除源程序,包括插入所需要的代码,由gcc后端处理并最终输出对应硬件平台的汇编码,源程序无需手工修改便可实现插装功能。

gcc的入口点main函数在文件main.c中。此函数非常简单,只有一条直接调用toplev_main函数的语句。toplev_main函数是在toplev.c文件中定义的,以下我们只关心与编译有关的源码,其他的暂时忽略。toplev_main中最重要的是调用了do_complile函数,这个函数从名字看就是做编译工作的;而在此之后,toplev_main函数就返回了。dd_compile函数也是在tokv.c中定义的,其中真正进行编译工作的是调用compilte_file函数。compik_file函数最终调用了一个钩子函数来分析(parse)整个输入文件:

(*lang_hooks.parse_file)(set_yydebug);

这里的lang_hooks是一个全局变量,不同语言的前端对此赋以不同的值。对c语言来说,这条语句相当于调用了c-opts.c中的c_common_parse_file函数。c_com-mon_parse_file中调用了c-parse.c中的c_parse_file函数;在此函数中又调用了同文件中的yyparse函数,该函数负责解析c语言源文件,并转化为特殊的语法树结构。该函数是gnu bison将yacc转变为c语言而自动生成的,所以这段代码阅读起来比较困难,但我们并不关心语法分析的细节。在完成函数体的分析后,利用已经建立的tree结构生成rtl,优化后最终输出汇编码;自此c函数的编译就算结束了,这些是由yyparse调用finish_function函数完成的。finish_function函数中最重要的函数是tree_rest_of_compilation(定义在tree_optimize.c中),它是真正实现上述功能的函数。为了说明它所做的具体事情,我们将该函数做了删减,保留了关键的地方。

将函数各个部分展开成rtl形式后,调用函数rest_of_compilation将rtl输出为汇编码。至此,得到了一张清晰的gcc编译时的函数调用路线。

2 基于gcc的程序插装技术

根据插装测试的要求,需要在函数开始时为每个参数调用钩子函数,并用钩子函数的返回值更新参数的值;同时,将被插装函数的名称压入函数本地栈内,作为该函数的一个匿名本地变量,只用于传递给钩子函数。从上面列出的tree_rest_of_compilation函数源码得知,负责建立被编译函数参数和返回值的函数是expand_function_start,定义是在文件function.c中。expand_function_start中处理函数参数和返回值的函数是assign_parms,这是需要特别关注的函数。

斜体加粗的部分是增加的代码。在for循环前,获得当前编译的函数名(见源码中①位置);但暂时不能输出到函数的rtl链中,因为本地栈要在所有参数传递完毕才完全建立起来。在for循环体结束前,记录下函数参数的

相关IC型号

热门点击

 

推荐技术资料

声道前级设计特点
    与通常的Hi-Fi前级不同,EP9307-CRZ这台分... [详细]
版权所有:51dzw.COM
深圳服务热线:13692101218  13751165337
粤ICP备09112631号-6(miitbeian.gov.cn)
公网安备44030402000607
深圳市碧威特网络技术有限公司
付款方式


 复制成功!