C/C++ 编译命令捕获机制及实现
基于编译实现的 SAST 工具,需要能够获取原生项目的构建命令,这通常需要使用到命令捕获相关的技术。这是本文所探究的内容。
主要探究在 Windows 和 Linux 中,涉及到 C/C++ 和 Java 的编译捕获的场景。其他非编译的语言,不需要捕获,其他可能需要编译的,也类似。
1. 主要的已知编译命令捕获框架及工具
这部分罗列一些主要的已知编译命令捕获的框架或者工具,如果觉得有价值,可以参考使用。
strace/ptrace
适用于 linux,strace/ptrace 是 linux 原生提供的接口和命令,可以捕获所有的命令。目前已知有多款工具都是基于 strace/ptrace 实现。
因为是 linux 原生提供的接口,所以应该也不涉及什么 license 的问题,我觉得。
bear
bear 可以直接捕获并生成 clang 的 compile_commands.json,基于 linux 的 LD_PRELOAD 实现,因此也是只适用于 linux。
协议不是很友好,没办法直接用。
compiledb
专门用于 make 类型的构建任务,有点儿像是应用 make 的假编动作。因此可以用在 windows 上面,但是应用也有点儿受限。
协议不是很友好,没办法直接用。
msbuild-database[1]
可以用于使用 msbuild 构建的所有的任务,因为 msbuild 是 visual studio 内置的一个构建工具,因此只要是 visual studio 开发的任务,都可以使用该工具捕获构建命令。
该版本是在原生工具的基础上做了修改的,支持链接命令的获取,还是很值得使用的。
协议友好,就是需要依赖特定的 .net 版本,有点儿烦人,在对外交付的时候,建议做好封装。
CodeChecker
在 CodeChecker 中,也默认支持了一个编译命令捕获的工具,该工具和 bear 实现机制完全一致,但是协议很友好。如果是想自己实现,建议抛弃 bear,选用 CodeChecker。
clang-power-tools[2]
可以读取 visual studio 配置中的信息,直接生成 clang 的编译数据库。
构建工具本身能力
比如 cmake、bazel、ninja 等,可以直接导出 clang 的编译数据库,也算是性能提升的途径,但是并不完善,导出来的数据,有时候有问题。
2. 编译命令捕获相关技术栈
下面,从个人工作经验角度,给出一个相对比较完整的,自己实现一款 SAST 工具的编译捕获实现技术栈:
图 1 C/C++编译捕获命令技术栈
基于 hook 的方式捕获编译命令(首选)
在 linux 中,可以基于 LD_PRELOAD 捕获命令,在 windows 中,可以基于 Detours[3] 等 Microsoft 开源的 hook 工具捕获命令。
当前典型的商用工具,都是基于该方法实现的,比如 Coverity。该方式的主要优点:
(1) 实现简单
(2) 捕获准确,能够完整地体现用户的构建过程
(3) 使用方便
当然,该方式有一些局限,这里列出一部分:
(1) 沙箱、跨进程、通过拉起 docker 构建等方式无法支持;
(2) 必须完整构建,如果不执行 clean 动作,有可能无法捕获到完整命令。
如果一定要说有什么缺点,那就是可能有点儿慢,如果用户构建需要两个小时,那就需要等两个小时了。
基于构建工具原生能力(补充)
基于 cmake、ninja、bazel 等直接导出 clang 编译数据库,虽然有一些信息缺失或者数据错误,但是挡不住快。
其他不推荐使用的有点儿用的方法
比如:
(1) 从构建日志中提取编译命令,比如一些构建任务,构建时会打印编译命令;
(2) 从配置文件中提取编译命令,比如从 visual studio 的 sln、vcproj 等配置中获取。
参考
[1] https://github.com/UnitTestBot/msbuild-database
[2] https://github.com/Caphyon/clang-power-tools/tree/master
[3] https://github.com/microsoft/Detours
版权声明: 本文为 InfoQ 作者【maijun】的原创文章。
原文链接:【http://xie.infoq.cn/article/d789d98205ed900b36fcd8fff】。
本文遵守【CC-BY 4.0】协议,转载请保留原文出处及本版权声明。
评论