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序列。

3.2 IR格式转换