静态分析C语言生成函数调用关系的利器——cally和egypt

大纲

  • 准备工作
  • 安装graphviz
  • 安装cally
  • 安装egypt
  • 简单分析
    • GCC产生RTL(Register transfer language)文件
    • cally
    • egypt
    • 总结
  • 高级分析
    • cally
    • egypt
  • 总结
  • 参考资料

在《静态分析C语言生成函数调用关系的利器——cflow》和《静态分析C语言生成函数调用关系的利器——cflow(二)》中,我们介绍了使用cflow直接分析c语言源码导出调用栈的方法。在做实验的过程中,我一直在思考一个问题:cflow能解释C语言?看了下源码后,发现它的确有解析的模块。大家可以看下它的部分代码。

// parser.c
typedef struct {char *name;int type_end;int parmcnt;int line;enum storage storage;
} Ident;void parse_declaration(Ident*, int);
void parse_variable_declaration(Ident*, int);
void parse_function_declaration(Ident*, int);
……
static void
print_token(TOKSTK *tokptr)
{switch (tokptr->type) {case IDENTIFIER:case TYPE:case WORD:case MODIFIER:case STRUCT:case PARM_WRAPPER:case QUALIFIER:case OP:fprintf(stderr, "`%s'", tokptr->token);break;case LBRACE0:case LBRACE:fprintf(stderr, "`{'");break;case RBRACE0:case RBRACE:fprintf(stderr, "`}'");break;case EXTERN:fprintf(stderr, "`extern'");break;case STATIC:fprintf(stderr, "`static'");break;case TYPEDEF:fprintf(stderr, "`typedef'");break;case STRING:fprintf(stderr, "\"%s\"", tokptr->token);break;default:fprintf(stderr, "`%c'", tokptr->type);}
}

可以发现它是纯纯的文本解析。这就引发了我的一个担忧:如果C语言的编译器对文件的解释和cflow的解释器对同一份文件的结果解析不同怎么办?这个可能性还是存在的。
本文介绍的cally和egypt就很好的避开了这个问题,因为对文件的解析交给了GCC编译器。它们只是对编译器产生的中间结构化内容(Register transfer language)进行解释和整理,这个难度就比解析C语言源码要简单。产出的DOT (graph description language)文件交给dot程序生成调用栈的图。
在这里插入图片描述
我们还是以《静态分析C语言生成函数调用关系的利器——cflow(二)》中的libevent库为例。

准备工作

安装graphviz

sudo apt install graphviz

安装cally

cally就是一个python脚本,我们只要把工程代码下载下来即可。

git clone https://github.com/chaudron/cally.git

安装egypt

wget https://www.gson.org/egypt/download/egypt-1.11.tar.gz .
tar xzf egypt-1.11.tar.gz
rm egypt-1.11.tar.gz
cd egypt-1.11
perl Makefile.PL
make
sudo make install
cd -

简单分析

GCC产生RTL(Register transfer language)文件

libevent库中的test-time程序是通过链接编译完的libevent.a和libevent_core.a生成的。现在我们不能依赖原工程中的cmake来生成,需要自己编写编译指令。(还是需要先把整个工程编译一遍,具体见《静态分析C语言生成函数调用关系的利器——cflow(二)》中坑3:缺失编译时产生的文件)。

gcc ./test/test-time.c \-I./build/include/ -I./include -I./ \-L./build/lib/ -Wl,-Bstatic -levent -levent_core -Wl,-Bdynamic \-o test-time-main

上面的脚本可以正确将test-time.c编译成可执行文件。
现在我们只要让它产出RTL文件即可。

gcc ./test/test-time.c \-I./build/include/ -I./include -I./ \-L./build/lib/ -Wl,-Bstatic -levent -levent_core -Wl,-Bdynamic \-fdump-rtl-expand

这样就产生了一个名字叫a-test-time.c.245r.expand的RTL文件。

cally

将上一步生成的文件拷贝到cally.py所在的目录,然后执行

python3 ./cally.py a-test-time.c.245r.expand |  dot -Grankdir=LR -Tpng -o cally_test_time_call_graph.png

请添加图片描述

egypt

egypt a-test-time.c.245r.expand --include-external |  dot -Grankdir=LR -Tpng -o egypt_test_time_call_graph.pn
g

请添加图片描述

总结

我们看下test-time.c的部分源码。可以看到egypt的展现更加准确,因为它将time_cb和main进行了关联,而cally则没展现出来这层关系。

static int
rand_int(int n)
{return evutil_weakrand_(&weakrand_state) % n;
}static void
time_cb(evutil_socket_t fd, short event, void *arg)
{struct timeval tv;int i, j;called++;if (called < 10*NEVENT) {for (i = 0; i < 10; i++) {j = rand_int(NEVENT);tv.tv_sec = 0;tv.tv_usec = rand_int(50000);if (tv.tv_usec % 2 || called < NEVENT)evtimer_add(ev[j], &tv);elseevtimer_del(ev[j]);}}
}int
main(int argc, char **argv)
{struct event_base *base;struct timeval tv;int i;#ifdef _WIN32WORD wVersionRequested;WSADATA wsaData;wVersionRequested = MAKEWORD(2, 2);(void) WSAStartup(wVersionRequested, &wsaData);
#endifevutil_weakrand_seed_(&weakrand_state, 0);if (getenv("EVENT_DEBUG_LOGGING_ALL")) {event_enable_debug_logging(EVENT_DBG_ALL);}base = event_base_new();for (i = 0; i < NEVENT; i++) {ev[i] = evtimer_new(base, time_cb, event_self_cbarg());tv.tv_sec = 0;tv.tv_usec = rand_int(50000);evtimer_add(ev[i], &tv);}i = event_base_dispatch(base);printf("event_base_dispatch=%d, called=%d, EVENT=%d\n",i, called, NEVENT);if (i == 1 && called >= NEVENT) {return EXIT_SUCCESS;} else {return EXIT_FAILURE;}
}

我们看到上面图片并没有展现诸如event_add这类外部函数的底层调用栈。这是因为这些函数是作为静态库提供给test-time进行链接的。且我们并没有生成它们的RTL文件,所以不能分析出完整的调用关系。
为了展现更加具体的调用关系,我们将进行一些改造,以获得更多RTL进行分析。

高级分析

上面问题的根源在于test-time编译依赖于静态库,我们首先要解决这个问题,就是要手撸一条可用的编译指令。
这个实验的主要难点也是在这个指令的正确书写,中间修正的过程我就不表了,直接贴出结果。

gcc `find . -regextype posix-extended -regex '^./[^/]*\.c$' ! -name 'wepoll.c' ! -name 'win32select.c' ! -name 'evthread_win32.c' ! -name 'buffer_iocp.c' ! -name 'bufferevent_async.c' ! -name 'arc4random.c' ! -name 'event_iocp.c' ! -name 'bufferevent_mbedtls.c'` \./test/test-time.c \-I./build/include/ -I./include -I./ \-L./build/lib/ -lcrypto -lssl \-DLITTLE_ENDIAN -D__clang__ \-UD_WIN32 -UDMBEDTLS_SSL_RENEGOTIATION \-fdump-rtl-expand

这样我们得到一堆RTL文件。这些文件我都将其拷贝到cally和egypt测试工程的sample目录下。

cally

python3 ./cally.py ../sample/*.expand --caller main |  dot -Grankdir=LR -Tsvg -o cally_full_test_time_call_graph.svg

生成文件非常大,就不展示了。(见https://github.com/f304646673/tools/tree/main/cally)
只展示event_add函数的调用栈。
在这里插入图片描述

egypt

egypt sample/*.expand --include-external --callees main |  dot -Grankdir=LR -Tsvg -o egypt_full_test_time_call_graph.svg

生成文件非常大,就不展示了。(见https://github.com/f304646673/tools/tree/main/egypt)
只展示event_add函数的调用栈。
在这里插入图片描述

总结

egypt比cally优秀,可以分析出更加复杂的调用关系。

参考资料

  • https://www.gson.org/egypt/
  • https://www.gson.org/egypt/egypt.html
  • https://github.com/chaudron/cally
  • https://ftp.gnu.org/gnu/cflow/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/427971.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Django笔记(六):DRF框架

首 前后端分离是互联网应用开发的标准使用方式&#xff0c;让前后端通过接口实现解耦&#xff0c;能够更好的进行开发和维护。 RESTful接口常见规范 在接口设计中&#xff0c;大家遵循一定的规范可以减少很多不必要的麻烦&#xff0c;例如url应有一定辨识度&#xff0c;可以…

电信宽带配置动态域名和端口映射

需求: 家宽映射动态域名访问内网服务 动态域名:18081>电信光猫:18081>Openwrt软路由:18081>主机192.168.3.172:8081 目前网络结构&#xff1a; 电信光猫192.168.1.1 Openwrt软路由192.168.3.1 主机192.168.3.172上8081端口起了一个nginx-docker服务 前置条件&#x…

HarmonyOS自定义弹出对话框CustomDialog并传递变量

HarmonyOS定义了一系列弹窗反馈类的组件​ 和前端开发框架VUE3配套生态库element plus中的提供各种组件相比&#xff0c;还是要少一些。可能是手机端操作和PC端操作的差异导致的​ 如果内置的弹窗不满足要求&#xff0c;可以基于CustomDialog自定义出各种个性化的反馈组件。 首…

【开源】基于JAVA语言的假日旅社管理系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 系统介绍2.2 QA 问答 三、系统展示四、核心代码4.1 查询民宿4.2 新增民宿评论4.3 查询民宿新闻4.4 新建民宿预订单4.5 查询我的民宿预订单 五、免责说明 一、摘要 1.1 项目介绍 基于JAVAVueSpringBootMySQL的假日旅社…

【自动化测试】Pytest+Appium+Allure 做 UI 自动化的那些事

文本主要介绍下 PytestAllureAppium 记录一些过程和经历。 法主要用了啥: Python3 Appium Allure-pytest Pytest Appium 不常见却好用的方法 Appium 直接执行 adb shell 方法 #Appium 启动时增加 --relaxed-security 参数 Appium 即可执行类似adb shell的方法 appium -p 4…

蓝凌OA sysUiExtend.do 任意文件上传漏洞复现

0x01 产品简介 蓝凌核心产品EKP平台定位为新一代数字化生态OA平台,数字化向纵深发展,正加速构建产业互联网,对企业协作能力提出更高要求,蓝凌新一代生态型OA平台能够支撑办公数字化、管理智能化、应用平台化、组织生态化,赋能大中型组织更高效的内外协作与管理,支撑商业…

TCP 异常断开连接【重点】

参考链接 https://xiaolincoding.com/network/3_tcp/tcp_down_and_crash.html https://xiaolincoding.com/network/3_tcp/tcp_unplug_the_network_cable.html#%E6%8B%94%E6%8E%89%E7%BD%91%E7%BA%BF%E5%90%8E-%E6%9C%89%E6%95%B0%E6%8D%AE%E4%BC%A0%E8%BE%93 关键词&#xff1a…

2023 IoTDB Summit:中核武汉核电运行技术股份有限公司主管工程师方华建《IoTDB在核电数字化转型过程的应用实践》...

12 月 3 日&#xff0c;2023 IoTDB 用户大会在北京成功举行&#xff0c;收获强烈反响。本次峰会汇集了超 20 位大咖嘉宾带来工业互联网行业、技术、应用方向的精彩议题&#xff0c;多位学术泰斗、企业代表、开发者&#xff0c;深度分享了工业物联网时序数据库 IoTDB 的技术创新…

代码随想录 Leetcode637. 二叉树的层平均值

题目&#xff1a; 代码(首刷自解 2024年1月24日&#xff09;&#xff1a; class Solution { public:vector<double> averageOfLevels(TreeNode* root) {vector<double> res {};if(root nullptr) return res;queue<TreeNode*> que;TreeNode* cur root;que…

Linux简介

Unix的特点&#xff1a; Unix很简洁&#xff0c;Unix只提供几百个系统调用&#xff0c;并且每个调用都有明确的目的。一切皆文件&#xff0c;对数据和对文件都是通过相同的系统调用接口&#xff1a;open&#xff08;&#xff09;&#xff0c;read&#xff08;&#xff09;&…

Vue2 - keep-alive 作用和原理

目录 1&#xff0c;介绍和作用2&#xff0c;原理3&#xff0c;使用场景3.1&#xff0c;效果展示3.2&#xff0c;实现思路 1&#xff0c;介绍和作用 <!-- 非活跃的组件将会被缓存&#xff01; --> <keep-alive><component :is"activeComponent" />…

hadoop集群规划部署

一、集群规划 三台硬件资源&#xff0c;部署hadoop版本&#xff0c;hadoop-3.3.5 &#xff0c;部署后配置文件。 Hadoop配置文件分两类&#xff1a;默认配置文件和自定义配置文件。 hadoop102hadoop103hadoop104HDFS NameNode DataNode DataNode SecondaryNameNode DataN…