美文网首页
LLVM(1)LLVM了解

LLVM(1)LLVM了解

作者: 迷心迷 | 来源:发表于2019-03-30 18:34 被阅读0次

一、什么是LLVM

1、官网:https://llvm.org/
The LLVM Project is a collection of modular and reusable compiler and toolchain technologies.
LLVM项目是模块化、可重用的编译器以及工具链技术的集合
美国计算机协会 (ACM) 将其2012 年软件系统奖项颁给了LLVM,之前曾经获得此奖项的软件和技术包括:Java、Apache、 Mosaic、the World Wide Web、Smalltalk、UNIX、Eclipse等等

2、创始人
Chris Lattner,亦是Swift之父

3、有些文章把LLVM当做Low Level Virtual Machine(低级虚拟机)的缩写简称,官方描述如下
The name "LLVM" itself is not an acronym; it is the full name of the project. “LLVM”这个名称本身不是首字母缩略词; 它是项目的全名

二、传统的编译器架构

1.png
  • Frontend:前端
    词法分析、语法分析、语义分析、生成中间代码
  • Optimizer:优化器
    中间代码优化
  • Backend:后端
    生成机器码

三、LLVM架构

2.png

不同的前端后端使用统一的中间代码LLVM Intermediate Representation (LLVM IR)

  • 如果需要支持一种新的编程语言,那么只需要实现一个新的前端
  • 如果需要支持一种新的硬件设备,那么只需要实现一个新的后端
  • 优化阶段是一个通用的阶段,它针对的是统一的LLVM IR,不论是支持新的编程语言,还是支持新的硬件设备,都不需要对优化阶段做修改
  • 相比之下,GCC的前端和后端没分得太开,前端后端耦合在了一起。所以GCC为了支持一门新的语言,或者为了支持一个新的目标平台,就 变得特别困难
  • LLVM现在被作为实现各种静态和运行时编译语言的通用基础结构(GCC家族、Java、.NET、Python、Ruby、Scheme、Haskell、D等)

四、Clang

1、什么是Clang?
LLVM项目的一个子项目
基于LLVM架构的C/C++/Objective-C编译器前端
官网:http://clang.llvm.org/

2、相比于GCC,Clang具有如下优点

  • 编译速度快:在某些平台上,Clang的编译速度显著的快过GCC(Debug模式下编译OC速度比GGC快3倍)
  • 占用内存小:Clang生成的AST所占用的内存是GCC的五分之一左右
  • 模块化设计:Clang采用基于库的模块化设计,易于 IDE 集成及其他用途的重用 - - 诊断信息可读性强:在编译过程中,Clang 创建并保留了大量详细的元数据 (metadata),有利于调试和错误报告
  • 设计清晰简单,容易理解,易于扩展增强

3、Clang与LLVM


3.png

五、OC源文件的编译过程

1、命令行查看编译的过程:$ clang -ccc-print-phases main.m


4.png

2、查看preprocessor(预处理)的结果:

$ clang -E main.m

3、词法分析,生成Token:

$ clang -fmodules -E -Xclang -dump-tokens main.m
5.png

4、语法分析,生成语法树(AST,Abstract Syntax Tree):

$ clang -fmodules -fsyntax-only -Xclang -ast-dump main.m
6.png 7.png

5、LLVM IR

  • LLVM IR有3种表示形式(但本质是等价的,就好比水可以有气体、液体、固体3种形态)
    text:便于阅读的文本格式,类似于汇编语言,拓展名.ll,
    $ clang -S -emit-llvm main.m
    memory: 内存格式
    bitcode:二进制格式,拓展名.bc,
    $ clang -c -emit-llvm main.m
    6、IR基本语法
    注释以分号 ; 开头
    全局标识符以@开头,局部标识符以%开头
    alloca,在当前函数栈帧中分配内存
    i32,32bit,4个字节的意思
    align,内存对齐
    store,写入数据
    load,读取数据

官方语法参考
https://llvm.org/docs/LangRef.html

8.png

六、源码

1、源码下载
下载LLVM
$ git clone https://git.llvm.org/git/llvm.git/
大小 648.2 M,仅供参考
下载clang
$ cd llvm/tools $ git clone https://git.llvm.org/git/clang.git/
大小 240.6 M,仅供参考

2、源码编译

  • 安装cmake和ninja(先安装brew,https://brew.sh/)
    $ brew install cmake $ brew install ninja

  • ninja如果安装失败,可以直接从github获取release版放入【/usr/local/bin】中 https://github.com/ninja-build/ninja/releases

  • 在LLVM源码同级目录下新建一个【llvm_build】目录(最终会在【llvm_build】目录下生成【build.ninja】)
    $ cd llvm_build $ cmake -G Ninja ../llvm -DCMAKE_INSTALL_PREFIX=LLVM的安装路径
    更多cmake相关选项,可以参考: https://llvm.org/docs/CMake.html

  • 依次执行编译、安装指令
    $ ninja
    编译完毕后, 【llvm_build】目录大概 21.05 G(仅供参考)
    $ ninja install
    安装完毕后,安装目录大概 11.92 G(仅供参考)

  • 也可以生成Xcode项目再进行编译,但是速度很慢(可能需要1个多小时)

  • 在llvm同级目录下新建一个【llvm_xcode】目录
    $ cd llvm_xcode $ cmake -G Xcode ../llvm

    9.png
10.png

七、应用与实践

八、clang插件开发

clang插件开发1 – 插件目录
1、在【clang/tools】源码目录下新建一个插件目录,假设叫做【ios-plugin】


11.png

2、在【clang/tools/CMakeLists.txt】最后加入内容: add_clang_subdirectory(ios-plugin),小括号里是插件目录名


12.png

3、在【ios-plugin】目录下新建一个【CMakeLists.txt】,文件内容是:add_llvm_loadable_module(IOSPlugin IOSPlugin.cpp)
IOSPlugin是插件名,IOSPlugin.cpp是源代码文件


13.png

4、编写插件源码
IOSPlugin.cpp

#include <iostream>
#include "clang/AST/AST.h"
#include "clang/AST/ASTConsumer.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/FrontendPluginRegistry.h"

using namespace clang;
using namespace std;
using namespace llvm;
using namespace clang::ast_matchers;

namespace IOSPlugin {
    class IOSHandler : public MatchFinder::MatchCallback {
    private:
        CompilerInstance &ci;
        
    public:
        IOSHandler(CompilerInstance &ci) :ci(ci) {}
        
        void run(const MatchFinder::MatchResult &Result) {
            if (const ObjCInterfaceDecl *decl = Result.Nodes.getNodeAs<ObjCInterfaceDecl>("ObjCInterfaceDecl")) {
                size_t pos = decl->getName().find('_');
                if (pos != StringRef::npos) {
                    DiagnosticsEngine &D = ci.getDiagnostics();
                    SourceLocation loc = decl->getLocation().getLocWithOffset(pos);
                    D.Report(loc, D.getCustomDiagID(DiagnosticsEngine::Error, "M了个J:类名中不能带有下划线"));
                }
            }
        }
    };
    
    class IOSASTConsumer: public ASTConsumer {
    private:
        MatchFinder matcher;
        IOSHandler handler;
        
    public:
        IOSASTConsumer(CompilerInstance &ci) :handler(ci) {
            matcher.addMatcher(objcInterfaceDecl().bind("ObjCInterfaceDecl"), &handler);
        }
        
        void HandleTranslationUnit(ASTContext &context) {
            matcher.matchAST(context);
        }
    };
    
    class IOSASTAction: public PluginASTAction {
    public:
        unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &ci, StringRef iFile) {
            return unique_ptr<IOSASTConsumer> (new IOSASTConsumer(ci));
        }
        
        bool ParseArgs(const CompilerInstance &ci, const vector<string> &args) {
            return true;
        }
    };
}

static FrontendPluginRegistry::Add<IOSPlugin::IOSASTAction>
X("IOSPlugin", "The IOSPlugin is my first clang-plugin.");


5、利用cmake生成的Xcode项目来编译插件(第一次编写完插件,需要利用cmake重新生成一下Xcode项目)
插件源代码在【Sources/Loadable modules】目录下可以找到,这样就可以直接在Xcode里编写插件代码
选择IOSPlugin这个target进行编译,编译完会生成一个动态库文件

6、在Xcode项目中指定加载插件动态库:BuildSettings > OTHER_CFLAGS -Xclang -load -Xclang 动态库路径 -Xclang -add-plugin -Xclang 插件名称

7、Hack Xcode

  • 首先要对Xcode进行Hack,才能修改默认的编译器
    下载【XcodeHacking.zip】,解压,修改【HackedClang.xcplugin/Contents/Resources/HackedClang.xcspec】的内容,设置一下自己编译好的clang的路径


    14.png
  • 然后在XcodeHacking目录下进行命令行,将XcodeHacking的内容剪切到Xcode内部
    $ sudo mv HackedClang.xcpluginxcode-select -print-
    path`/../PlugIns/Xcode3Core.ideplugin/Contents/SharedSupport/Developer/Library/Xcode/Plug-ins

$ sudo mv HackedBuildSystem.xcspec xcode-select -print- path/Platforms/iPhoneSimulator.platform/Developer/Library/Xcode/Specifications
`

8、修改Xcode的编译器


15.png

9、编译项目
编译项目后,会在编译日志看到IOSPlugin插件的打印信息(如果插件更新了,最好先Clean一下项目)

10、更多
想要实现更复杂的插件功能,就需要利用clang的API针对语法树(AST)进行相应的分析和处理

关于AST的资料
https://clang.llvm.org/doxygen/namespaceclang.html p https://clang.llvm.org/doxygen/classclang_1_1Decl.html p https://clang.llvm.org/doxygen/classclang_1_1Stmt.html

九、推荐书籍

16.png

相关文章

  • LLVM(1)LLVM了解

    一、什么是LLVM 1、官网:https://llvm.org/The LLVM Project is a col...

  • iOS 逆向 day 18 GCC LLVM Clang

    一、LLVM 1. 什么是 LLVM 官网:https://llvm.org/ The LLVM Project ...

  • llvm 编译

    1,下载llvm 源码git clone https://git.llvm.org/git/llvm.git/2,...

  • vim的YouCompleteMe插件安装Clang支持

    1. 下载llvm的源代码 wget http://llvm.org/releases/3.6.0/llvm-3....

  • LLVM

    LLVM 什么是LLVM? 官网:https://llvm.org/ The LLVM Project is a ...

  • iOS_LLVM

    LLVM 官网:https://llvm.org/[https://llvm.org/] The LLVM Pro...

  • 基于LLVM开发属于自己Xcode的Clang插件

    开发的插件效果如下 简介 本开发是基于LLVM,那么我们先来简单了解一下LLVM: LLVM项目是模块化、可重用的...

  • 浅谈LLVM

    何为LLVM 在LLVM的官网(https://llvm.org/[https://llvm.org/])中写到T...

  • 二十五、LLVM

    什么是编译器 LLVM概述 LLVM案例体验 LLVM源码 & 编译流程 1 什么是编译器? 1.1 Python...

  • iOS混淆调研-基于llvm

    关于iOS混淆-基于llvm 在此之前,你需要先了解 llvm 源码: https://github.com/mi...

网友评论

      本文标题:LLVM(1)LLVM了解

      本文链接:https://www.haomeiwen.com/subject/yfpsbqtx.html