从零开始构建LLVM Sanitizer:编译器与运行时分析实战

本文详细记录了在LLVM框架中实现动态指针分析工具的过程,涵盖编译器驱动架构、Sanitizer运行时集成机制,以及如何通过LLVM Pass实现代码插桩的技术要点。

从希望与梦想构建LLVM Sanitizer

Trail of Bits每年举办为期一个月的冬季实习计划。2019年,我们迎来了4位实习生参与3个项目。本文作者Carson Harmon是普渡大学的新毕业生,专注于编译器和系统工程领域,现已成为我们研究团队的正式成员。

项目初衷

我最初计划在LLVM中实现动态指针分析(points-to analysis)。该技术能够确定程序中指针所指向的内存数据类型及分配位置,对于验证静态分析准确性、增强分析上下文以及追踪对象生命周期具有重要意义。

LLVM Sanitizer基础架构

LLVM Sanitizer框架是实现此类分析的理想平台,它提供:

  • 编译时代码插桩能力
  • 包含libc实现的运行时支持库
  • 函数替换接口
  • 内存/线程管理系统
  • 操作系统特定系统调用支持

现有广泛使用的检测工具均基于该框架实现,例如:

  • AddressSanitizer:检测非法堆栈/堆访问、释放后使用等内存错误
  • MemorySanitizer:发现未初始化读取
  • LeakSanitizer:定位内存泄漏
  • UndefinedBehaviorSanitizer:捕获未定义行为(如整数溢出)

构建自定义Sanitizer的技术路径

由于文档缺乏,我通过以下资源逆向工程LLVM工作机制:

  • Eli Bendersky的博客和GitHub仓库
  • Adrian Sampson博士的博客
  • EuroLLVM会议论文
  • LLVM工具链官方文档

核心组件交互关系

  1. 编译器驱动架构
    作为各模块间的粘合剂,负责传递代码生成过程中的所有信息。Sanitizer开发可能需要修改:

    • 类型生成流程
    • Pass调度逻辑
    • 运行时库链接机制

    ![Clang前端、LLVM IR、compiler-rt与驱动的关系图]

  2. Pass管理机制
    LLVM Pass通过修改IR实现指令插桩。Sanitizer Pass应最后执行以避免被优化Pass影响。驱动根据前端配置注册Pass的执行顺序。

  3. 运行时组件集成
    compiler-rt库支持两种交互方式:

    • 通过转换Pass插入运行时函数调用
    • 使用函数钩子接口劫持目标程序执行

实践指南

我已创建包含以下内容的完整教程:

  • 预构建测试Sanitizer示例
  • Pass与运行时组件开发分步指南
  • LLVM开发实用资源

冬季实习是假期学习的绝佳机会,希望更多企业能开展类似项目。

[Recent Posts内容未翻译]

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计