1.Tensorflow 编译加速器 XLA 源码深入解读
2.Hikari源码分析 - AntiDebug
3.Obfuscator-llvm源码分析
4.毕升编译器优化:Lazy Code Motion
5.LLVM IR 指南
6.编译技术入门与实践之LLVM概述及环境构建
Tensorflow 编译加速器 XLA 源码深入解读
XLA是源码Tensorflow内置的编译器,用于加速计算过程。分析然而,源码不熟悉其工作机制的分析开发者在实践中可能无法获得预期的加速效果,甚至有时会导致性能下降。源码本文旨在通过深入解读XLA的分析源码猿著源码,帮助读者理解其内部机制,源码以便更好地利用XLA的分析性能优化功能。
XLA的源码源码主要分布在github.com/tensorflow/tensorflow的多个目录下,对应不同的分析模块。使用XLA时,源码可以采用JIT(Just-In-Time)或AOT( Ahead-Of-Time)两种编译方式。分析JIT方式更为普遍,源码对用户负担较小,分析只需开启一个开关即可享受到加速效果。源码本文将专注于JIT的实现与理解。
JIT通过在Tensorflow运行时,从Graph中选择特定子图进行XLA编译与运行,实现了对计算图的加速。Tensorflow提供了一种名为JIT的使用方式,它通过向Tensorflow注册多个优化PASS来实现这一功能。这些优化PASS的执行顺序决定了加速效果。
核心的优化PASS包括但不限于EncapsulateXlaComputationsPass、MarkForCompilationPass、EncapsulateSubgraphsPass、BuildXlaOpsPass等。EncapsulateXlaComputationsPass负责将具有相同_xla_compile_id属性的算子融合为一个XlaLaunch,而XlaLaunch在运行时将子图编译并执行。
AutoClustering则自动寻找适合编译的子图,将其作为Cluster进行优化。aop源码入口XlaCompileOp承载了Cluster的所有输入和子图信息,在运行时通过编译得到XlaExecutableClosure,最终由XlaRunOp执行。
在JIT部分,关键在于理解和实现XlaCompilationCache::CompileStrict中的编译逻辑。此过程包括两步,最终结果封装在XlaCompilationResult和LocalExecutable中,供后续使用。
tf2xla模块负责将Tensorflow Graph转化为XlaCompilationResult(HloModuleProto),实现从Tensorflow到XLA的转换。在tf2xla中定义的XlaOpKernel用于封装计算过程,并在GraphCompiler::Compile中实现每个Kernel的计算,即执行每个XlaOpKernel的Compile。
xla/client模块提供了核心接口,用于构建计算图并将其转换为HloModuleProto。XlaBuilder构建计算图的结构,而XlaOpKernel通过使用这些基本原语描述计算过程,最终通过xla_builder的Build方法生成HloComputationProto。
xla/service模块负责将HloModuleProto编译为可执行的Executable。该过程涉及多个步骤,包括LLVMCompiler的编译和优化,最终生成适合特定目标架构的可执行代码。此模块通过一系列的优化pass,如RunHloPasses和RunBackend,对HloModule进行优化和转换,最终编译为目标代码。
本文旨在提供XLA源码的深度解读,帮助开发者理解其工作机制和实现细节。如有问题或疑问,盛图源码欢迎指正与交流,共同探讨和学习。期待与您在下一篇文章中再次相遇。
Hikari源码分析 - AntiDebug
一、框架分析 针对PASS的具体实现进行深入分析。该PASS旨在提升编译后程序的抵抗调试能力,其核心逻辑包括两个主要方面: 链接预编译的反调试IR代码 特定于平台的内联汇编注入 针对Darwin操作系统上的AArch架构,若未找到ADBCallBack和InitADB函数,PASS会尝试直接注入内联汇编代码。该代码片段可能利用系统调用,如ptrace,来检测是否处于调试环境。 此外,配置允许用户指定预编译反调试IR文件的路径和函数混淆概率。 具体实现包括: 检查预编译IR路径,构建默认路径并链接预编译的IR文件。 修改ADBCallBack和InitADB函数属性,确保它们在编译和链接阶段表现出反调试行为。 初始化标志和目标三元组信息,准备为每个模块提供初始化和链接预编译IR的过程。 模块处理和函数处理涉及应用概率值来决定是否对模块和函数应用反调试混淆。 预编译的反调试IR文件包含了一系列用于反调试的函数和结构,如检测调试器的代码、修改执行路径以规避调试跟踪、以及插桩代码以检测异常行为。 通过LLVM工具链中的llvm-dis工具,可以将.bc文件转换为可读的LLVM IR文件。该文件结构包含多个结构体定义、全局声明、鸿蒙openharmony源码函数实现和属性。 函数ADBCallBack简单地终止程序并执行无法到达的指令。函数InitADB执行系统调用和检查来检测调试状态,可能涉及进程信息查询、动态库加载、系统调用、内存分配、异常端口检查等操作。 系统调用声明确保了程序能调用各种底层函数进行操作,如sysctl、dlopen、dlsym、task_get_exception_ports、isatty、ioctl等。 总结,通过在编译器优化阶段插入反调试逻辑,相较于源代码实现,基于LLVM Pass的AntiDebug方法提供了更好的隐蔽性、可移植性、灵活性、维护性和混淆程度。然而,这种方法需要对LLVM框架有深入理解,可能增加构建和调试复杂度。Obfuscator-llvm源码分析
在逆向分析中,Obfuscator-llvm是一个备受关注的工具,它通过混淆前端语言生成的中间代码来增强SO文件的安全性。本文主要讲解了Obfuscator-llvm的prism源码解读三个核心pass——BogusControlFlow、Flattening和Instruction Substitution,它们在O-llvm-3.6.1版本中的实现。
BogusControlFlow通过添加虚假控制流和垃圾指令来混淆函数,其runOnFunction函数会检查特定参数,如混淆次数和基本块混淆概率。在测试代码中,它会将基本块一分为二,插入随机指令,形成条件跳转,如“1.0 == 1.0”条件下的真跳转和假跳转。
Flattening通过添加switch-case语句使函数结构扁平化,runOnFunction会检查启动标志。在示例代码中,它将基本块分隔,创建switch结构,并根据随机值跳转到不同case,使函数执行流程变得复杂。
Instruction Substitution负责替换特定指令,runOnFunction会检测启动命令,遍历所有指令并随机应用替换策略,如Add指令的多种可能替换方式。
虽然O-llvm提供了一定程度的混淆,但仍有改进空间,比如增加更多的替换规则和更复杂的跳转策略。作者建议,利用O-llvm的开源特性,开发者可以根据需求自定义混淆方法,提高混淆的复杂性和逆向难度。
最后,对于对Obfuscator-llvm感兴趣的读者,可以参考《ollvm的混淆反混淆和定制修改》的文章进一步学习。网易云安全提供的应用加固服务提供了试用机会,对于保护软件安全具有实际价值。
更多关于软件安全和源码分析的内容,欢迎访问网易云社区。
毕升编译器优化:Lazy Code Motion
本文介绍代码移动(插入)方法以消除冗余计算的典型应用。在优化程序中,通过移动或插入代码,可以避免重复计算,同时保持程序行为不变。通过程序流图分析,首先需识别冗余计算的路径,进而确定优化范围。优化过程中,需关注临界边,即源基本块有多条后继,目标基本块有多条前驱的连接边,以避免可能的安全问题。为处理这种边上的安全问题,引入合成块,确保插入代码时程序行为不变。本文详细介绍了算法的四个关键定义,以及如何根据这些定义确定插入点。
算法首先定义预期表达式(Anticipated),指在该点后所有路径都计算出该表达式值的情况。通过后向分析,确定哪些点可以作为预期表达式的插入点。接着定义将可用的表达式(Will-be-Available),指在该点前是预期表达式的点,且后续路径中没有计算出该表达式的点。通过前向分析,找出将可用表达式的插入点集合。结合这两个集合,可以确定最早可插入表达式的集合。
通过进一步定义延缓表达式(Postponable),算法可确定最晚插入点,以减少寄存器压力。最晚插入点位于表达式被首次计算后,且在被后续使用前的路径上。最后,引入已用表达式(Used Expressions)定义,用于消除当前块之外的临时变量赋值,以优化代码。最终解决方案是在满足特定条件的基本块开头插入表达式,并替换所有相关计算,以此达到消除冗余计算的目的。
在算法介绍后,还涉及LLVM源码中具体实现的查看,包括`MachineCSE`类与`NaryReassociatePass`等类的实现,以提供深入理解与实践参考。本文通过理论分析与实例解释,旨在提供一种消除冗余计算的优化方法,减少程序执行的资源消耗,提高代码执行效率。
LLVM IR 指南
LLVM IR是一种通用的程序表示形式,编程语言编译器通过前端生成并经过一系列分析和转换(称为pass)生成优化后的IR。这种表示允许跨语言和硬件的隔离,便于优化,并支持在不同阶段进行优化,比如runtime时,IR会被保留并在发现可优化点时进行重新编译。
LLVM IR有三种形式:内存中的ir、硬盘上的bitcode文件(ir.bc)和供人阅读的文本形式(ir.ll)。在编译过程中,ir的内存格式用于全阶段优化,特别是在需要runtime优化时。
LLVM工具链包含了编译LLVM源码所需的工具,通常在编译目录的bin目录下。要生成IR的基本结构,可以使用clang命令。IR的基本结构由module、function、basicblock和instruction组成,每个模块可能包含多个函数,每个函数由多个基本块构成,体现了控制流的执行逻辑。
LLVM的Pass Manager执行分析和转换,包括analysis pass和transform pass。新旧Pass Manager在结构和命令行使用上有所不同。Pass的执行顺序通常从module开始,逐步深入到function、loop等层次,涉及到如别名分析、MemorySSA和Loop-Invariant-code-motion等优化策略。
例如,别名分析分析变量的load/store操作产生的别名,通过构建语句间的约束和迭代生成alias,提供函数间的内存依赖信息。MemorySSA则在此基础上,提供内存依赖查询,便于IR的分析和transform pass。
Link-time优化是LLVM的另一大优势,它允许在链接阶段对整个程序的IR进行优化,利用内存中的IR进行更深入的分析和改进。这比传统编译过程中的优化更为灵活和高效。
调试和命令行使用方面,LLVM提供了丰富的工具和技巧,帮助开发者在编译过程中进行调试和优化,比如MakeFile中的关键语句和调试技巧。
编译技术入门与实践之LLVM概述及环境构建
LLVM入门与实践:概述与环境搭建
本系列旨在记录学习过程,便于知识整理和交流。作为一名专注于智能芯片研究的工程师,编译器设计是常遇课题,最近的实验涉及LLVM pass,处理源代码到数据流的转换。
LLVM是一个广泛应用于编译器和工具链的开源项目,它以SSA(静态单一赋值)为基础,支持多种语言的编译。该项目由伊利诺大学发起,包含核心库、编译器、调试器等组件,以通用性、灵活性和可重用性为特点。LLVM的核心子项目包括LLVM Core(优化器和代码生成器)、Clang(C/C++编译器)、LLDB(调试器)等,每个子项目都服务于特定的编译任务和性能优化。
要进行LLVM pass实验,首先需要获取LLVM源代码。推荐的命令和依赖环境设置需确保对应支持的硬件平台和软件库,包括CMake、gcc、Python等。在Ubuntu系统上,可能需要特别关注OpenSSL的安装,并可能需要升级CMake。通过Docker环境如Chipyard进行实验是一个不错的选择,环境检查和配置构建的过程也需遵循官方文档指导。
构建LLVM和Clang时,可以使用CMake或make工具,根据需要选择并行构建或顺序构建。在遇到编译错误时,官方文档和论坛提供了相关帮助资源。完成环境配置和编译后,LLVM的工具和库将被安装到指定位置,便于后续的开发和实验。