构建LLVM项目

前文介绍了LLVM这么多,那么我们能用LLVM做一款自己的编程语言么?答案是,有点难度,但不是不可行。只要你熟悉C++编程,而且有足够的热情,那么就没有什么能阻止你了。

下面我就来介绍一下,LLVM项目的基本方法。

需要的东西: LLVM库,文档,CMAKE,C++编译器

LLVM编译器架构

编译器技术,作为计算机科学的皇后,从诞生起,就不断推进着计算机科学的发展,编译器的发展史,简直就是计算机发展史的缩影,而编译器的架构也逐句变得更加优雅,独立性更强。但说到编译器的架构,可能还留存着编译原理课程的印象:

5个经典流程:词法分析 -> 语法分析 -> 语义分析 -> 中间代码优化 -> 目标代码生成

一般的,我们会将编译器分为前端和后端,前端负责处理源代码,后端负责目标代码生成。

但软件工程,就是在不断的抽象和分层,分层解决问题是重要的特点,分层能够增加层之间的独立性,更好的完成任务。

llvm::raw_fd_ostream类详解

llvm::raw_fd_ostream在头文件llvm/Support/raw_ostream.h中声明。

共有以下6种构造方法:

  • raw_fd_ostream::raw_fd_ostream (StringRef Filename, std::error_code & EC)
    打开指定的文件(Filename)进行写入。如果发生错误,则将有关错误的信息输入EC,并应立即销毁这个stream。作为一种特殊情况,如果文件名是“-”,那么这个stream将使用STDOUT_FILENO而不是打开文件。这将不会关闭stdout描述符。

  • raw_fd_ostream::raw_fd_ostream (StringRef Filename, std::error_code & EC, sys::fs::CreationDisposition Disp)
    CreationDisposition是枚举类型,共有4种:

    1. CD_CreateAlways(打开文件时,如果它已经存在,截断它;如果它还不存在,创建一个新文件)
      截断文件的意思是打开文件的时候先将文件的内容清空,再进行写入;并不是删除文件。
    2. CD_CreateNew(打开文件时,如果它已经存在,fail;如果它还不存在,创建一个新文件)
    3. CD_OpenExisting(打开文件时,如果它已经存在,则打开文件,并将偏移量设置为0;如果它还不存在,fail)
    4. CD_OpenAlways
      (打开文件时,如果它已经存在,则打开文件,并将偏移量设置为0;如果它还不存在,创建一个新文件)
  • raw_fd_ostream::raw_fd_ostream (StringRef Filename, std::error_code & EC, sys::fs::FileAccess Access)
    FileAccess枚举类型:FA_Read和FA_Write。

  • raw_fd_ostream::raw_fd_ostream (StringRef Filename, std::error_code & EC, sys::fs::OpenFlags Flags)
    Flags允许可选flags来控制文件将如何打开。枚举类型,共有9种:OF_None、F_None、OF_Text(以文本模式打开)、F_Text、OF_Append(以追加模式打开)、F_Append、OF_Delete(关闭时删除文件,只对windows有影响)、OF_ChildInherit(启动子进程时,此文件应在子进程中保持打开状态)、OF_UpdateAtime(强制文件在访问时更新,只对windows有影响)。

  • raw_fd_ostream::raw_fd_ostream (StringRef Filename, std::error_code & EC, sys::fs::CreationDisposition Disp, sys::fs::FileAccess Access, sys::fs::OpenFlags Flags)
    如上

  • raw_fd_ostream::raw_fd_ostream (int fd, bool shouldClose, bool unbuffered = false)
    FD是它要写入的文件描述符。如果ShouldClose为true,则在stream销毁时关闭文件。但是如果FD是stdout或stderr,它将不会关闭。

使用llvm实现一门语言cava

背景

cava产生的背景,是由于ha3业务方对插件定制及版本兼容需求,要求我们基于llvm开发一种性能与c++相当的类java脚本语言。

经过我们的调查发现:

可备选项由例如sp上的lua,elasticsearch上的groovy等,但最终得出的结论是现有的脚本语言都不能很好的满足ha3的需求。

基于LLVM的字符串加密的实现

前置要求

  • 对C++/C的入门知识
  • 高中及以上的英语水平
  • 一台电脑
  • 对命令行的基础使用
  • 对垃圾代码的忍耐能力
  • 会用Google
  • 会下载安装编译LLVM

基础知识

  • 每个源码文件(大致上)对应一个翻译单元(Translation Unit),LLVM体系中每个Module对应一个翻译单元
  • Module是LLVM中间表示的最外层封装,所有的函数,变量,元数据等等全部封装在对应的Module中
  • (几乎)所有跟LLVM中间表示有关的数据类型都继承自llvm::Value,下文中所有的值指代的都是该类所有子类的统称
  • 字符串在LLVM中间表示里用一个常数数组实现。(但有对应的基于字符串的构造方法)
  • 每个函数由一个或多个基本块组成,每个基本块有一个结束指令用于改变控制流。调用其他函数,跳转至其他基本块,返回void或其他值的指令都属于此列。

LLVM的去平坦化

—看见while和switch不是”迷宫”,就是”虚拟”。

为了保护程序虚拟机是最常用的一种手段,并不是说用了多复杂的算法。主要是会花费大量的时间来把程序还原为本来的样子。使用OLLVM项目中的控制流平坦化、虚假控制流、指令替换等混淆策略会进一步加大分析的难度。

Ollvm是什么

llvm是一个底层虚拟机,OLLVM(Obfuscator-LLVM)是瑞士西北应用科技大学安全实验室于2010年6月份发起的一个项目,这个项目的目标是提供一个LLVM编译套件的开源分支,能够通过代码混淆和防篡改,增加对逆向工程的难度,提供更高的软件安全性。目前,OLLVM已经支持LLVM-4.0.1版本;所使用的编译器是clang。

使用LLVM创建有效的编译器

LLVM Pass

LLVM以其提供的优化特性而著名。优化被实现为Pass。这里需要注意的是 LLVM为您提供了使用最少量的代码创建实用阶段 (utility pass)的功能。例如,如果不希望使用 “hello”作为函数名称的开头,那么可以使用一个实用Pass来实现这个目的。

了解 LLVM opt 工具

opt手册页中可以看到,opt命令是模块化的LLVM优化器和分析器。一旦您的代码支持定制Pass,您将使用 opt把代码编译为一个共享库并对其进行加载。如果您的 LLVM 安装进展顺利,那么opt 应该已经位于您的系统中。opt 命令接受 LLVM IR(扩展名为 .ll)和LLVM 位码格式(扩展名为 .bc),可以生成 LLVM IR或位码格式的输出。下面展示了如何使用 opt 加载您的定制共享库:

$ opt –load=mycustom_pass.so –help –S

使用LLVM创建一个可以工作的编译器

LLVM是一种非常强大的编译器基础架构框架,专门为使用您喜爱的编程语言编写的程序的编译时、链接时和运行时优化而设计。LLVM可运行于若干个不同的平台之上,它以能够生成快速运行的代码而著称。

LLVM 框架是围绕着代码编写良好的中间表示 (IR)而构建的。本文将深入讲解 LLVM IR的基础知识以及它的一些微妙之处。在这里,您将构建一个可以自动为您生成LLVM IR 的代码生成器。拥有一个LLVM IR生成器意味着您所需要的是一个前端以供插入您所喜爱的编程语言,而且这还意味着您拥有一个完整的流程(前端解析器 + IR 生成器 + LLVM 后端)。创建一个自定义编译器会变得更加简单。

LLVM初探

LLVM简介

The LLVM Project is a collection of modular and reusable compiler and toolchain technologies.

LLVM是模块化、可重用的编译器以及工具链的集合,有些人把LLVM当成是一个低层的虚拟机(low level virtual machine),不过LLVM官方给出的解释是这样的:

The name “LLVM” itself is not an acronym; it is the full name of the project.

也就是说LLVM并不是一个缩写,而是整个项目的全名。LLVM和传统的编译器(GCC)是有差别的。