Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2336666
  • 博文数量: 527
  • 博客积分: 10343
  • 博客等级: 上将
  • 技术积分: 5565
  • 用 户 组: 普通用户
  • 注册时间: 2005-07-26 23:05
文章分类

全部博文(527)

文章存档

2014年(4)

2012年(13)

2011年(19)

2010年(91)

2009年(136)

2008年(142)

2007年(80)

2006年(29)

2005年(13)

我的朋友

分类: C/C++

2012-07-03 09:53:14

Hydra意思是九头蛇, 这是作者查看Mozilla项目的CFG(Control Flow Graph)映入脑海的一个印象, 所以该项目叫 Dehydra, 意思是处理这种复杂调用关系.

代码随DeHydra一起发布的是TreeHydra. 默认编译 DeHydra时, 会同时编译TreeHydra. TreeHydra比 DeHydra更高级, 也更复杂, 基于GCC的GIMPLE中间代码表示. 两者都是把gcc工作过程中某个阶段的中间结果以JavaScript对象的形式, 提供给用户以JavaScript写的静态分析代码.

DeHydra和TreeHydra都必需与支持插件机制的GCC一起使用, 最早能支持的版本可能是4.2, 推荐用4.5, 但没具体说明4.5.0到4.5.3之间的具体哪个版本, 但在mozilla网站的build例子中, 看到以4.5.3 为例.

整个build过程应该是比较麻烦的, 需要build:

GCC 4.5 with plugin enabled, 

SpiderMondy javascript 引擎

DeHydra & TreeHydra

源代码以 mercurial版本控制工具管理, 所以你还得安装这个软件. 整个需要下载的源代码也非常大. 所以我这里推荐的办法是:

直接使用 Ubuntu 11, 因为它自带的是 GCC 4.6.1, 支持插件功能, 经我试用, 可以与 DeHydra/TreeHydra搭配使用. 需要安装的软件包有:

* SpiderMonkey

* DeHydra

这两个软件包默认都是没有安装的, 不过这是二进制安装, 使用 Ubuntu的包管理工具非常便利的就能安装上.

SpiderMonkey是一个动态库形式的JavaScript引擎, 它被GCC的插件机制在编译期调用.

DeHydra是以动态库形式和用作静态分析的javascript文件发布. 调用时需要用以下命令行:

gcc -fno-builtin -fplugin=/usr/share/dehydra/libs/gcc_dehydra.so -fplugin-arg-gcc_dehydra-script=static_analyze.js  -c test.cpp -o null

其中 -fno-builtin 不是必需, 不过我发现在一个例子中, 使用静态分析尝试截获对 strcpy的调用时, 没有成功, 原因是gcc默认使用内置的memcpy来代替对C库中strcpy的调用. 使用了 -fno-builtin之后, 就会使用C库中的strcpy而不是内置版本了.

-fplugin= 后面的.so文件就是 dehydra包提供的动态库文件. 注意这个包没有提供 treehydra, 而手工编译dehydra是会同时编译出gcc_treehydra.so的.

静态分析的javascript文件是应用的核心, 其它功能都是这套工具本身提供的, 正是通过javascript文件, 用户可以定制自己的静态分析规则.

有4个回调函数可以使用:

process_decl(decl)

process_function(decl, body)

process_type(type)

input_end()

具体使用以及每个javascript对象的可用属性/方法, 可以参考 https://developer.mozilla.org/En/Dehydra/Object_Reference

下面是我的一个测试例子 test.js :

function process_function(decl, body)
{
    let var_decl_name = decl.name;
    print(var_decl_name);
    for each(let bodyItem in body) {
        for each(let stmtItem in bodyItem.statements) {
            let stmtStr = ("stmt at " + stmtItem.loc + " :" + stmtItem.name + " in " + var_decl_name);
            if (stmtItem.isFcall) {
                stmtStr += "function arguments: " + stmtItem.arguments;
            }
            if (stmtItem.isReturn) {
                stmtStr += ". Return!" ;
            }
            error(stmtStr);
        }
    }
}

error函数是内置可用的函数, 用于向gcc的编译过程添加一条错误信息.

下面是这个分析产生的输出:

test.cpp: In function ‘void g()’:
test.cpp:24:1: error: stmt at test.cpp:6:6 :foo() in g()function arguments:
test.cpp: In function ‘int main()’:
test.cpp:24:1: error: stmt at undefined :undefined in main()
test.cpp:24:1: error: stmt at undefined :undefined in main()
test.cpp:24:1: error: stmt at undefined :undefined in main()
test.cpp:24:1: error: stmt at undefined :undefined in main()
test.cpp:24:1: error: stmt at undefined :undefined in main()
test.cpp:24:1: error: stmt at test.cpp:17:8 :p in main()
test.cpp:24:1: error: stmt at test.h:2:9 :T::i in main()
test.cpp:24:1: error: stmt at test.cpp:19:10 :a in main()
test.cpp:24:1: error: stmt at /usr/include/string.h:128:14 :strcpy(char*, const char*) in main()function arguments: ({shortName:"a", assemblerName:"_ZZ4mainE1a", name:"a", type:{isArray:true, max:"4u", type:#1={min:{value:"-128", type:#1#}, max:{value:"127", type:#1#}, isSigned:true, precision:8, name:"char"}}, loc:{_source_location:765480, file:"test.cpp", line:19, column:10}}),({value:"\"12\"", type:{variantOf:{isArray:true, max:"2u", type:#1={min:{value:"-128", type:#1#}, max:{value:"127", type:#1#}, isSigned:true, precision:8, name:"char"}}, isArray:true, max:"2u", type:{variantOf:#1#, isConst:true, min:{value:"-128", type:#1#}, max:{value:"127", type:#1#}, isSigned:true, precision:8, name:"char"}}})
test.cpp:24:1: error: stmt at test.cpp:6:6 :foo() in main()function arguments:
test.cpp:24:1: error: stmt at test.cpp:10:6 :g() in main()function arguments:
test.cpp:24:1: error: stmt at undefined :undefined in main(). Return!
test.cpp:24:1: error: stmt at undefined :undefined in main(). Return!
foo()
g()
main()

 

被分析的源程序是:

 

01: #include "test.h"
02: #include
03: #include
04: #include
05:
06: void foo()
07: {
08: }
09:
10: void g()
11: {
12:     foo();
13: }
14:
15: int main()


16: {
17:     T  p ;
18:     p.i = 5;
19:     char a[] = "asdf";
20:     strcpy(a, "12");
21:     foo();
22:     g();
23:     return 0;
24: }

头文件 test.h 是

01: struct T {
02:     int i;
03:     int j;
04: private:
05:     char * p;
06: };
07:
08: T * foo(int i);
09:
10: enum Color
11: {
12:     red,
13:     blue,
14:     yellow
15: };

 

可以看出目前 DeHydra(0.9版)还是有一些局限和不便的:

1. 对于return语句, 不知道为什么对应2个statement

2. 对于函数调用, 其.loc参数返回的不是该函数被调用时的行号, 而是该函数被定义时的行号, 我在参考文档里没有找到函数调用语句在被调用行的行号

3. 若.js有非法语句调用, gcc调用过程就crash了, 不够健壮.

粗浅试用, 眼下的局限也许是了解不深的原因.

对于 TreeHydra还没有试用.

阅读(7928) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~