如何基于LLVM获取机器码级别的控制流图(CFG)?
获取LLVM机器码级别的控制流图(CFG)的可行方法
当然有靠谱的办法!LLVM在后端代码生成的整个流程中都维护着机器码级别的CFG,下面给你分享几种实用的方式:
1. 用LLVM的llc工具直接查看或导出机器码CFG
这是最快捷的方式,不需要写额外代码:
- 可视化展示:使用
-view-machine-cfg选项,LLVM会自动弹出图形窗口展示机器码CFG。步骤如下:- 先把你的高级语言源码编译成LLVM IR:
clang -O0 -S -emit-llvm your_source.c -o your_source.ll - 用
llc处理IR并触发CFG可视化:
这个选项依赖Graphviz,如果没安装的话需要先装一下(比如Ubuntu上llc -view-machine-cfg your_source.llapt install graphviz)。
- 先把你的高级语言源码编译成LLVM IR:
- 文本形式导出:如果不需要可视化,用
-print-machine-cfg选项可以把CFG输出到终端或文件:
输出内容会包含每个机器基本块(Machine Basic Block)的编号、指令,以及块之间的跳转关系。llc -print-machine-cfg your_source.ll > machine_cfg.txt
2. 通过LLVM后端API程序化获取CFG
如果你需要在代码里处理机器码CFG,可以写一个LLVM Machine Function Pass来遍历和提取信息:
- LLVM的
MachineFunction类对应一个函数的机器码表示,里面包含多个MachineBasicBlock(MBB)实例,每个MBB就是CFG的一个节点。 - 每个MBB提供
predecessors()和successors()方法,可以直接获取它的前驱和后继块,从而构建完整的CFG结构。 - 简单的实现思路:继承
MachineFunctionPass,在runOnMachineFunction方法里遍历所有MBB,逐个收集它们的邻接关系,然后输出或存储为你需要的格式。
3. 查看Machine IR(MIR)阶段的CFG
LLVM后端会先生成基于SSA的机器IR(也就是你提到的“基于SSA的机器码”),这个阶段的CFG和最终机器码的CFG结构几乎一致,只是指令还没经过寄存器分配等最终优化。你可以用llc输出MIR来查看:
llc -stop-after=machine-scheduler -o - your_source.ll
输出的MIR中会清晰标注每个MBB的跳转指令和块之间的依赖关系,也能帮你理解机器码CFG的生成过程。
补充说明
如果你已经有了编译好的目标文件,也可以用llvm-objdump反汇编后,通过分析跳转指令(比如x86的jmp、je等)手动或用脚本构建CFG,但这种方式不如直接用LLVM后端工具/API准确,因为反汇编会丢失LLVM内部的一些块标记信息。
内容的提问来源于stack exchange,提问作者soham
相关产品推荐
相关产品推荐

