LLVM|LLVM入门的入门
https://llvm.org/docs/ProgrammersManual.html#introduction
https://llvm.org/docs/LangRef.html
2022年11月16日
:heart:
0.前言
对程序进行分析,不管是静态还是动态,都需要插桩。从LLVM入手插桩很方便,师兄花了很长时间研究,实验室只有他孤军奋战、苦苦求索,备受孤独与迷茫的双重煎熬。导师坦言担心他的精神状态。
之后,师兄每周给我们开小灶,进行鞭挞:cry:
之所以称为入门的入门,是因为我还没真正入门:smile:
我还不能系统地写明LLVM的种种,本文只对其进行简要介绍,引导志同道合的朋友。同时,在前面的引用奉上官方文档,希望共同进步。
1.LLVM简介
编译原理把编译过程分为前后端:
- 前端把源代码翻译成中间表示(IR)
- 后端把IR编译成目标平台的机器码。当然IR也可以直接给解释器解释执行。
经典的编译器如gcc
在设计上提供一条龙服务,即我们不需要知道它的IR是什么,它也不提供接口给我们来操作其IR。这种做法有好有坏。好处是从前端到后端,这些编译器的代码强耦合,可以在内部做任何想做的优化。坏处是,每当一个新平台出现,这些编译器都要各自为政实现一个从IR到新平台的后端。如果出现一种新语言,那么可能就需要实现一个新的编译器,重新设计一个新的IR。如果有M种新语言,N种新平台,那么最坏的情况下,需要实现M*N个前后端,比较低效。
为此,我们希望有一种共用的IR,每当有一种新语言,就添加其到IR的前端,每当有一个新平台,就添加其到IR的后端。如果有M种新语言,N种新平台,最优情况下我们只需要实现M+N个前后端。
LLVM就是这样一个项目。LLVM的核心设计了一个叫LLVM IR的中间表示,并以库的方式提供一系列接口,提供各种功能。
2.Clang简介
Clang是一个基于LLVM的编译器驱动,它提供把C/C++/OC等语言翻译成LLVM IR的前端,并使用LLVM的库实现LLVM IR到目标平台的后端。
叫编译器驱动是因为使用clang main.c -o main
(Clang兼容gcc语法)的时候,Clang帮我们“驱动”C语言预处理器,C语言前端,LLVM后端,链接器等等。其实GUN gcc也是编译器驱动。为了方便,我们习惯称为编译器。
一般我读Clang为/see-lang/,但是其实很多人读/klang/,哈哈。
3.LLVM IR(Intermediate Representation)
LLVM的中间表⽰,本质上⼀种与源编程语⾔和⽬标机器架构⽆关的通用中间表⽰
LLVM IR是⼀种类似于RISC的低级虚拟指令集
LLVM是使⽤简单类型系统的强类型(例如, i32
是⼀个32位整数, i32*
是指向32位
整数的指针)
LLVM IR 不使用一组固定的命名寄存器,它使⽤⼀个名为%字符的无限临时集合
LLVM IR代被设计成三种不同的形式:内存编译器IR,磁盘⼆进制.bc
表⽰(适合于即时编
译器的快速加载),可读的汇编语⾔.ll
3.1 IR结构
Module(模块)是⼀份LLVM IR的顶层容器,对应于编译前端的每个翻译单元(TranslationUnit)。每个模块由⽬标机器信息、全局符号(全局变量和函数)及元信息组成。
Function(函数)就是编程语⾔中的函数,包括函数签名和若干个基本块,函数内的第⼀个基本块叫做入口基本块。
BasicBlock(基本块)是⼀组顺序执⾏的指令集合,只有⼀个入口和⼀个出口,非头尾指令执行时不会违背顺序跳转到其他指令上去。每个基本块最后⼀条指令⼀般是跳转指令(跳转到其它基本块上去),函数内最后⼀个基本块的最后条指令是函数返回指令。
Instruction(指令)是LLVM IR中的最小可执行单位,每⼀条指令都单占一行
一个Module中可以有n个Function,Function内可以有n个BasicBlock,BasicBlock是单进单出的n条Instruction序列。