使用 opt 优化 LLVM IR,定制 clang 实现函数名加密

版权归作者所有,如有转发,请注明文章出处:https://cyrus-studio.github.io/blog/

1. LLVM IR

LLVM IR(Intermediate Representation) 是 LLVM 编译框架中的一种中间表示形式,它是一种面向低级的中间代码,是 LLVM 架构的核心部分。LLVM IR 既可以用作 LLVM 编译器的输入,也可以用作输出,供其他编译器或工具链使用。

LLVM IR 的两种存储形式:

  • LLVM IR 语言(文本形式,扩展名 .ll):可读的 LLVM IR 代码。

  • LLVM 位码(Bitcode,扩展名 .bc):二进制形式的 LLVM IR,主要用于高效存储和传输。

LLVM IR 是平台无关的,可以跨不同的硬件架构进行移植。

LLVM IR 语言参考手册:https://llvm.org/docs/LangRef.html

1.1 将 C 文件转换为 LLVM IR

生成文本形式的 LLVM IR(.ll 文件)

clang -S -emit-llvm hello.c -o hello.ll

以 hello.ll 为例,生成的内容大概如下

; Function Attrs: noinline nounwind optnone uwtable
; 这行注释了函数的属性
; - noinline: 该函数不会被内联(inline)。
; - nounwind: 该函数不会引发异常(不会 unwind stack)。
; - optnone: 编译器不会对此函数进行任何优化。
; - uwtable: 该函数有一个可用的异常处理表(unwind table)。

define dso_local i32 @main() #0 {
; define: 定义一个函数。
; dso_local: 该函数在本地动态库中可见(仅限于当前编译单元)。
; i32: 返回类型为 32 位整数。
; @main: 函数名为 'main'。
; #0: 函数使用属性组 0 中定义的属性(即上面注释的那一行)。

entry:
; entry: 函数的入口基本块(Basic Block)的标签。

  %retval = alloca i32, align 4
  ; alloca: 在栈上分配内存。这里分配了一个 32 位整数(i32)。
  ; align 4: 分配的内存对齐到 4 字节边界。
  ; %retval: 变量的名称(SSA 变量),用于存储 main 函数的返回值。

  store i32 0, ptr %retval, align 4
  ; store: 将一个值存储到内存地址中。
  ; i32 0: 要存储的值是 32 位整数 0(即 main 函数的返回值)。
  ; ptr %retval: 存储的位置是之前分配的栈变量 %retval。
  ; align 4: 存储操作按 4 字节对齐。

  %call = call i32 (ptr, ...) @printf(ptr noundef @"??_C@_0P@MHJMLPNF@Hello?0?5World?$CB?6?$AA@")
  ; call: 调用一个函数,这里调用了 C 标准库的 printf 函数。
  ; i32 (ptr, ...): printf 函数的原型。它接受一个指针(通常是 C 字符串格式化字符串)和可变参数列表(...)。
  ; ptr noundef @"??_C@_0P@MHJMLPNF@Hello?0?5World?$CB?6?$AA@": 这是一个字符串常量的地址(指针)。
  ; noundef: 表示传递给函数的参数不会是未定义的(undefined)。
  ; %call: 保存 printf 的返回值(返回打印的字符数)。

  ret i32 0
  ; ret: 返回指令,用于结束函数的执行。
  ; i32 0: main 函数返回 0(表示正常退出)。
}

1.2 生成二进制 LLVM IR(.bc 文件)

生成二进制 LLVM IR(.bc 文件)

clang -emit-llvm -c hello.c -o hello.bc

可以使用 llvm-dis 转换回文本形式

llvm-dis hello.bc -o hello.ll

1.3 使用 clang 编译 IR 文件

运行以下命令将 .ll 文件编译为可执行程序

clang hello.ll -o hello.exe

2. opt

opt 是 LLVM 工具链中的一个优化工具。它用于读取、优化和转换 LLVM IR 文件。

opt 工具通过应用各种优化 passes,可以显著提高程序的性能。

opt 的功能:

  • 优化:对 LLVM IR 进行一系列优化(如常量折叠、循环展开、函数内联等)。

  • 分析:生成各种分析报告(如控制流图、数据流分析)。

  • 转换:将 LLVM IR 从一种形式转换为另一种形式。

显示所有可用的优化 passes

opt --help

优化 LLVM IR 文件

opt -O3 hello.ll -o hello_opt.bc

参数说明:

  • -O3:表示应用最高级别的优化(与 clang 的 -O3 类似)。

  • -o hello_opt.bc:输出优化后的文件

生成可读的 LLVM IR 文件

opt -O3 hello.bc -S -o hello_opt.ll

参数说明:

  • -S:表示输出可读的文本格式(.ll 文件)。

应用特定的优化 pass,并查看优化效果。例如

opt -passes=mem2reg hello.ll -S -o hello_mem2reg.ll

参数说明:

  • -passes:应用特定的优化 pass

  • mem2reg:将栈上的内存变量提升为寄存器变量(即“提升” alloca 指令)。

  • -S:输出可读的 .ll 文件。

函数内联优化,将函数调用替换为函数体,从而减少函数调用的开销。

opt -passes=inline hello.ll -S -o hello_inline.ll

生成函数的控制流图(Control Flow Graph),输出为 .dot 文件,可以用 Graphviz 进行可视化。

opt -passes=dot-cfg hello.ll

3. 使用 Clion 调试 opt

使用 CLion 打开 llvm-project\llvm\CMakeLists.txt

word/media/image1.png

作为项目打开

word/media/image2.png

打开 CMake 设置,工具链使用 Visual Studio,点击应用

word/media/image3.png

等待 Cmake 执行完成后,可以看到 opt 的运行/调试配置

word/media/image4.png

编辑 opt 运行/调试配置,添加程序实参,比如

-O3 "D:\Projects\llvm-project\build\hello.ll" -o "D:\Projects\llvm-project\build\hello_opt.bc"

word/media/image5.png

找到 main 函数(llvm/tools/opt/opt.cpp),并下断点

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值