使用Python检测C文件中的递归

我需要在一个相当大的(5-15,000)C(不是C ++)文件集中检测直接和间接递归。

这些文件已经过预处理。

出于安全原因,代码非常“老派”,因此没有像函数指针那样的花哨的东西,只有传递变量的函数和一些执行相同操作的函数宏。

检测递归的最自然的方法是制作一个有向调用图,考虑每个函数一个边缘的节点将转到它调用的所有其他函数。 如果图形有任何周期,那么我们有递归。

查找函数调用的正则表达式是微不足道的,但我还需要知道调用哪个函数。

PyCParser很不错,但它抱怨很多东西,比如未定义的变量或者typedef,其中源类型没有在不同的文件中定义或定义,这在我的用例中完全不相关。 该项目使用自定义依赖管理系统,因此有些包含,并且这些是自动添加的,所以我需要PyCParser不关心FuncCallFuncDef节点以外的任何东西 ,我认为没有办法将解析过程本身限制为只是。

我宁愿不实现解析器,因为我没有时间学习如何在python中执行此操作然后实现解决方案。

回到问题,我将如何解析C文件中的函数? 基本上使用字符串(文件中定义的函数名称)作为键,以及字符串列表(每个函数调用的函数)作为值? 正则表达式似乎是最自然的解决方案。

使用python并不是可选的。

为什么不在编译的代码上使用objdump然后解析生成的程序集来构建图形?

test1.c文件:

 extern void test2(); void test1() { test2(); } 

test2.c文件:

 extern void test1(); void test2() { test1(); } int main() { test2(); } 

现在建立它:

 gcc -g test1.c test2.c -o myprog 

现在拆机

 objdump -d myprog > myprog.asm 

查看所有函数调用时使用几个简单的正则表达式,同时记住您正在使用的上下文。 反汇编示例显示了它应该是多么容易:

 00401630 <_test1>: 401630: 55 push %ebp 401631: 89 e5 mov %esp,%ebp 401633: 83 ec 08 sub $0x8,%esp 401636: e8 05 00 00 00 call 401640 <_test2> 40163b: c9 leave 40163c: c3 ret 40163d: 90 nop 40163e: 90 nop 40163f: 90 nop 00401640 <_test2>: 401640: 55 push %ebp 401641: 89 e5 mov %esp,%ebp 401643: 83 ec 08 sub $0x8,%esp 401646: e8 e5 ff ff ff call 401630 <_test1> 40164b: c9 leave 40164c: c3 ret 

然后使用python对你的反汇编进行后处理并构建一个function => calls的字典:

 import re import collections calldict = collections.defaultdict(set) callre = re.compile(".*\scall\s+.*<(.*)>") funcre = re.compile("[0-9a-f]+\s<(.*)>:") current_function = "" with open("myprog.asm") as f: for l in f: m = funcre.match(l) if m: current_function = m.group(1) else: m = callre.search(l) if m: called = m.group(1) calldict[current_function].add(called) 

我没有编写完整的图搜索,但您可以使用以下简单代码检测“乒乓”递归:

 for function,called_set in calldict.items(): for called in called_set: callset = calldict.get(called) if callset and function in callset: print(function,called) 

这给了我:

 _test2 _test1 _test1 _test2 

这个符号/ asm分析技术也用于callcatcher来检测未使用的C函数(这里也可以非常容易地通过检查不在任何集合中的键,对编译器符号进行一些过滤)