LLVM初探

编译 想必都知道,那么LLVM是什么?
LLVM是一种编译器!LLVM编译流程是怎么样的?
本篇就LLVM进行初探

首先让我们来了解编译器是神马~

一、编译器是什么?

1、以python程序为例
新建一个python文件:helloDemo.py(内部一行代码打印hello)

print("hello\n")

进入python文件,执行python helloDemo.py

image.png

2、c语言程序为例
新建helloDemo.c文件:

#include <stdio.h>
int main(int a,char * argv[]){
        printf("hello \n");
        return 0;
}

执行clang helloDemo.c编译,生成a.out文件,file a.out查看文件

image.png

发现最终生成文件是:.out文件:64位的Mach-O可执行文件,当前clang出来的是x86_64架构, 说明mac电脑可读。 所以可以./a.out直接执行:


image.png

以上可以看到,python和C输出流程是完全不同的,pthyon直接用python命令就可以输出结果,而C需要先生成.out文件。这是由于两种编译过程不一样

  • python是解释型语言,一边翻译一边执行,机器可直接执行。
  • C语言是编译型语言,不可以直接执行,需要编译器将其转换成机器识别语言。

注:
编译型语言:编译后输出的是指令(0、1组合),cpu可直接执行指令
解释性语言:生成的是数据,不是0、1组合,机器也能直接识别

  • 编译器就是将高级语言转换成机器可识别的语言可执行文件

补:
汇编语言是否需要编译?
直接解释
1、早期科学家,就是使用0、1编程。 为了摒弃手敲0和1的繁琐。开发者们创造了中间解释器,再创建出call、bl等这种容易记的指令来代表0、1组合(例如call对应00001111)。有了对应关系后,就好办了。程序员只用输入call、bl这样的标记指令,经过解释器,变成0和1的组合,就可以直接交给机器去执行了。 这就是汇编的由来。
2、基于汇编以上,再映射和封装相关对应关系。于是生成了跨时代性的c语言,再往上层封装,就出现了高级语言oc、swift、JAVA等语言。之所以汇编执行快,是因为它直接转换为机器语。
3、既然汇编速度这么快,为什么不都用汇编开发?
因为汇编的指令集,是针对同一操作系统而言,不支持跨平台。机器指令是cpu的在识别。早期的计算机厂家非常多,虽然都用0和1的组合,但相同组合背后却是相应不同的指令。所以汇编无法跨平台,不同操作系统下,汇编指令是不同的,所以需要可以跨平台的高级语言来开发~

二、LLVM概述

1、上面我们知道了编译器是什么。那么LLVM是什么呢?
llVM就是编译器的一种:

  • 架构编译器(compiler)的框架系统,以c++编写而成,用于优化以任意程序语言编写的程序的编译时间(compile-time)链接时间(link-time)运行时间(run-time)以及空闲时间(idle-time),对开发者保持开放,并兼任已有脚本
  • 2006年Chris Lattner加盟Apple Inc.并致力于LLVM在Apple开发体系中的应用。Apple也是LLVM计划的主要资助者
    目前LLVM已经被苹果iOS开发工具、Xilinx Vivado、Facebook、Google等各大公司采用

2、传统编译器


image.png
  • 编译器前端(Frontend)

编译器的前端任务是解析源代码。 会进行词法分析语法分析语义分析检查源代码是否存在错误,然后构建抽象语法树(Abstract Syntax Tree AST),LLVM前端还会生成中间代码(intermediate representation, IR)

  • 优化器(Optimizer)

优化器负责各种优化改善代码的运行时间,如消除冗余计算

源代码中有很多函数调用,就会需要需要申请很多函数调用栈空间,调用函数栈时,需要压栈输入数据,调用完毕后,出栈。其中过程逻辑可能非常复杂,那么就会无形中就会占用很多内存,优化器就是用来优化一些逻辑,以节省时间和空间。

  • 后端(Backkend)/ 代码生成器(CodeGenerator)

将代码映射到目标指令集生成机器语言,并进行机器相关的代码优化 (目标指不同操作系统)

3、iOS的编译器架构
Objective-C、C、C++编译器的前端用的都是Clang,Swift使用的是swift,后端使用的是llvm

image.png
三、LLVM的设计

1、llvm:支持多种语言多种硬件架构。使用通用代码表示形式:IR(用来在编译器中表示代码的形式)
⚠️GCC也是一个非常成功的编译器,但由于它作为整体应用程序设计的,用途受到了限制
因为llvm使用IR作为中间文件,所以

  • LLVM可以为任何编程语言独立编写前端,也可以为任何硬件架构独立编写后端.

  • LLVM是一个简单编译器,而是架构编译器,可以兼容所有前端后端

    image.png

2、Clang

ClangLLVM项目的一个子项目。基于LLVM架构的轻量级编辑器,诞生之初就是为了替代GCC,提供更快编译速度。 他是负责编译C、C++、Objecte-C语言的编译器,它属于整个LLVM架构中的编译器前端
对于开发者而言,研究Clang可以给我们带来很出益处

下面就Clang分析一下OC编译流程

3、编译流程

  • 新建一个.m文件


    image.png
  • 进入文件Test2
    执行命令clang -ccc-print-phases main.m

    image.png

此步骤为7步骤
0: 输入文件:找到源文件
1: 预处理:宏的展开,头文件的导入
2: 编译:词法、语法、语义分析,最终生成IR
3: 汇编: LLVM通过一个个的Pass去优化,每个Pass做一些事,最后生成汇编代码
4: 生成 目标文件
5: 链接: 链接需要的动态库和静态库,生成可执行文件
6:架构可执行文件:通过不同架构,生成对应的可执行文件
这里并没有出现optimizer优化器,因为它是独立触发,与流程阶段无关

  • 预处理
    在.文件里定义一个宏:C


    image.png

使用命令:clang -E main.m >> main2.m,生成main2.m文件
查看main2.m:大部分是stdio库的代码,定位到main函数里,发现
C变成了50了

image.png

得知,
预处理:􏳀􏰟􏳅􏳆􏲿􏰋􏳇􏲉􏲪􏲚􏲛􏰆􏲫􏲙􏲽􏲧􏰆􏲨􏲩􏰱完成2个步骤,

  1. 导入头文件 2.替换宏

还有一个定义类型的叫做typedef,这里我们把 int替换成wl_INT64,试试是否还原

image.png

预处理结束:


image.png

发现tydef并没有被还原

由此我们可以根据预处理结果,做一些安全管理方面的混淆措施

使用define,将重要方法名称或者类进行替换。比如用#define BUY XXXDemo
代码中使用宏BUY,被hank时,实际代码是XXXDemo,不易被发现。
且#define的真实内容,不应该写成乱码,会让人有此地无银三百两的感觉,最好弄成系统类似名称或其他不经意的名称。才会被忽视,安全级别会更高 。

  • 编译
    【1】词法分析
    使用clang -fmodules -fsyntax-only -Xclang -dump-tokens main.m
image.png

这些将代码拆分成一个个Token。标注了位置是第几行的第几个字符开始的。

【2】语法分析
是验证语法是否正确:

  • 在词法分析的基础上,将单词序列组合成各类语法短语,如“程序”,“语句”,“表达式”等,然后将所有节点组成抽象语法树(Abstract Syntax Tree,AST)。 语法分析程序判断源程序在结构上是否正确。
    使用clang -fmodules -fsyntax-only -Xclang -ast-dump main.m
image.png
  • 可以看到语法树。每一个操作都生成一个变量,作用域、数据类型、运算方式都在语法树体现出来。
  • 语法树一次只能处理一次计算两次运算,就得多分一层。
  • 语法分析,就是在生成语法树时完成检测的
    􏲀􏱓􏲫􏲙􏲪􏲚􏲛􏲜􏳁􏲉􏰊􏵷􏵸􏲿􏰋 如果头文件找不到,可以指定SDK
    clang -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator12.2.sdk(自己SDK路径) -fmodules -fsyntax-only -Xclang -ast-dump main.m
  • 生成中间代码IR(Intermediate representation)
    完成以上步骤后,就开始生成中间代码IR,代码生成器(Code Generation)会将语法树自顶向下遍历逐步翻译成LLVM的IR。通过下面命令生成.ll文本文件,查看IR代码:
    clang -S -fobjc-arc -emit-llvm main.m

    image.png

  • 代码在这一阶段进行runtime的桥接:property的合成,ARC处理等

IR的基本语法
@ 全局标识
% 局部标识
alloca 开辟空间
align 内存对齐
i32 32个bit,4个字节
store 写入内存
load 读取数据
call 调用数据
ret 返回

  • IR的优化
    llvm的优化级别分别是 􏰆􏰑􏰒􏳱􏳲􏱨􏳲􏰀 -O0 -O1 -O2 -O3 -Os(􏳳􏲲􏲣􏰀􏰼􏰌􏳴􏲚􏳎􏳵O)
    使用命令clang -Os -S -fobjc-arc -emit-llvm main.m -o main.ll

bitCode
Xcode7之后,开启bitCode苹果会做进一步的优化,生成.bc的中间代码,我们通过优化后的IR代码生成.bc文件
clang -emit-llvm -c main.ll -o main.bc

补:日常开发中,我们引入第三方库,有时会提示不支持bitcode,可以通过一、将源码编译一下,二、工程设置不支持.bitcode即可

  • 生成汇编代码
    我们通过最终生成的.bc或者.ll代生成汇编代码
    clang -S -fobjc-arc main.bc -o main.s
    clang -S -fobjc-arc main.ll -o main.s

    image.png

    汇编代码也可以进行优化clang -Os -S -fobjc-arc main.m -o main.s

  • 生成目标文件(汇编器)
    目标文件的生成,是汇编器以汇编代码作为输入,将汇编代码转换为机器代码,最后输出目标文件(object file)
    clang -fmodules -c main.s -o main.o

    image.png

    通过nm命令,查看下main.o中的符号:nm -nm main.o
    image.png

_printf 是一个是undefined external的
undefined表示在当前文件暂时找不到符号_printf
external表示这个符号是外部可以访问的。
以上打印结果表示是因为printf、PoolPop、PoolPush函数是来自于其他库,所以找不到,需要链接其他目标文件

  • 生成可执行文件(链接)􏱹􏰎􏲿􏳀􏰟􏲚􏲛􏱶􏰜􏰝􏱷
    连接器把编译产生的.o文件和(.dylib.a)文件,生成一个mach-o文件
    clang main.o -o main
    查看链接后的符号
    nm -nm main

四、重新编辑Clang插件
由于国内网络限制,我们需要借助镜像下载LLVM源码
https://mirror.tuna.tsinghua.edu.cn/help/llvm/

  • 在llvm的tools目录下下载Clang

cd llvm/tools
git clone https://mirrors.tuna.tsinghua.edu.cn/git/llvm/clang.git

  • 在LLVM的projects目录下下载compiler-rt􏰊libcxx、􏰊libcxxabi
  • 在clang的tools下安装extra工具
  • llvm编译,最新的只支持cmake来编译,所以需要安装cmake
  • 通过brew安装cmake
    brew install cmake

通过Xcode编译llvm

  • cmake编译成Xcode项目

一顿猛如虎的操作后,电脑没烧坏的情况下,就编译成功了,然后创建插件,请听下回分解~😄

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,542评论 6 504
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,822评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,912评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,449评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,500评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,370评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,193评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,074评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,505评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,722评论 3 335
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,841评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,569评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,168评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,783评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,918评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,962评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,781评论 2 354

推荐阅读更多精彩内容

  • 前言   计算机是只能直接理解机器语言,而不能直接理解高级语言的,所以计算机要执行高级语言编写的程序,就必须要把高...
    xxxxxxxx_123阅读 846评论 4 0
  • 一、编译器概述 编译器就是说将高级语言 翻译 为低级语言的程序。 二、LLVM概述   LLVM 是 构架编译器 ...
    脚踏实地的小C阅读 537评论 0 0
  • 目录 传统编译器设计 输入源代码(Obj-C, Swift, ...) → 编译器处理 → 输出机器码(01010...
    小瞎_MarkDash阅读 1,246评论 0 2
  • 前言 2000年,伊利诺伊大学厄巴纳-香槟分校(University of Illinois at Urbana-...
    星光社的戴铭阅读 15,896评论 8 180
  • 本文将简单介绍 Clang LLVM 相关的知识,然后介绍一下代码是如何一步步的编译运行的,以及可以利用 clan...
    z4ywzrq阅读 2,612评论 0 0