LLVM从小白到放弃(一)- LLVM概述与LLVM环境搭建

LLVM从小白到放弃(一)- LLVM概述与LLVM环境搭建

LLVM的历史

  • LLVM计划启动于2000年,开始由美国UIUC大学的Chris Lattner博士主持开展,后来Apple也加入其中。最初的目的是开发一套提供中间代码和编译基础设施的虚拟系统。
  • LLVM命名最早源自于底层虚拟机(Low Level Virtual Machine)的缩写,随着LLVM项目的不断发展,原先的全称已不再适用,目前LLVM就是该项目的全称。

什么是LLVM

  • 广义:LLVM是一个包括了很多模块的编译器框架。
  • 狭义:LLVM特指LLVM项目中的LLVM Core和Clang子模块。
  • 简单来收,可以将LLVM理解成为一个现代化、可扩展的编译器。

GCC的编译流程

  • GCC分为三个模块:前端、优化器和后端

暂时无法在文档外展示此内容

  • LLVM本质上也是三段式:

暂时无法在文档外展示此内容

LLVM的编译流程

  • 一个具体的例子:

暂时无法在文档外展示此内容

LLVM相对于GCC的优势

优势1:模块化
  • LLVM:LLVM是高度模块化设计的,每一个模块都可以从LLVM项目中抽离出来单独使用。
  • GCC:而GCC虽然也是三段式编译,但各个模块之间是难以抽离出来单独使用的。
优势2:可扩展
  • LLVM:LLVM为开发者提供了丰富的API,例如开发者可以通过LLVM Pass框架干预中间代码优化过程,并且配备了完善的文档
  • GCC:虽然GCC是开源的,但要在GCC的基础上进行扩展门槛很高、难度很大

LLVM编译过程总结

  • 对于C/C++程序来说,LLVM的编译过程如图所示:

LLVM环境搭建

Ubuntu/LLVM/CMake版本
  • Ubuntu 20.04
  • LLVM 12.0.1 / 9.0.9svn(ndkr21e)
  • Cmake 3.21.1
第一步:下载LLVM-Core和Clang源代码

https://github.com/llvm/llvm-project/releases/tag/llvmorg-12.0.1

clang-12.0.1.src.tar.xz

llvm-12.0.1.src.tar.xz

下载、解压、并重命名,并在同一目录下新建build文件夹,如下:

第二步:编译LLVM项目

在同一文件夹内创建build.sh文件,内容如下:

cd build 

cmake -G "Unix Makefiles" -DLLVM_ENABLE_PROJECTS="clang" -DCMAKE_BUILD_TYPE=Release -DLLVM_TARGETS_TO_BUILD="X86" -DBUILD_SHARED_LIBS=On ../llvm 

make 

make install

cmake 参数解释:

  • -G “Unix Makefiles”:生成Unix下的Makefile
  • -DLLVM_ENABLE_PROJECTS=“clang”:除了 LLVM Core 外,还需要编译的子项目。
  • -DLLVM_BUILD_TYPE=Release:在cmake里,有四种编译模式:Debug, Release, RelWithDebInfo, 和MinSizeRel。使用 Release 模式编译会节省很多空间。
  • -DLLVM_TARGETS_TO_BUILD=“X86”:默认是ALL,选择X86可节约很多编译时间。
  • -DBUILD_SHARED_LIBS=On:指定动态链接 LLVM 的库,可以节省空间。

LLVM基本用法

第一步:将源代码转化成LLVM IR
#include "iostream"



using namespace std;



int main() {

    cout << "Hello World!" << endl;



    return 0;

}

LLVM IR 有两种表现形式,一种是人类可阅读的文本形式,对应文件后缀为 .ll ;另一种是方便机器处 理的二进制格式,对应文件后缀为 .bc 。使用以下命令将源代码转化为 LLVM IR:

clang -S -emit-llvm hello.cpp -o hello.ll

clang -c -emit-llvm hello.cpp -o hello.bc

; ModuleID = 'hello.cpp'

source_filename = "hello.cpp"

target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"

target triple = "x86_64-unknown-linux-gnu"



%"class.std::ios_base::Init" = type { i8 }

%"class.std::basic_ostream" = type { i32 (...)**, %"class.std::basic_ios" }

%"class.std::basic_ios" = type { %"class.std::ios_base", %"class.std::basic_ostream"*, i8, i8, %"class.std::basic_streambuf"*, %"class.std::ctype"*, %"class.std::num_put"*, %"class.std::num_get"* }

%"class.std::ios_base" = type { i32 (...)**, i64, i64, i32, i32, i32, %"struct.std::ios_base::_Callback_list"*, %"struct.std::ios_base::_Words", [8 x %"struct.std::ios_base::_Words"], i32, %"struct.std::ios_base::_Words"*, %"class.std::locale" }

%"struct.std::ios_base::_Callback_list" = type { %"struct.std::ios_base::_Callback_list"*, void (i32, %"class.std::ios_base"*, i32)*, i32, i32 }

%"struct.std::ios_base::_Words" = type { i8*, i64 }

%"class.std::locale" = type { %"class.std::locale::_Impl"* }

%"class.std::locale::_Impl" = type { i32, %"class.std::locale::facet"**, i64, %"class.std::locale::facet"**, i8** }

%"class.std::locale::facet" = type <{ i32 (...)**, i32, [4 x i8] }>

%"class.std::basic_streambuf" = type { i32 (...)**, i8*, i8*, i8*, i8*, i8*, i8*, %"class.std::locale" }

%"class.std::ctype" = type <{ %"class.std::locale::facet.base", [4 x i8], %struct.__locale_struct*, i8, [7 x i8], i32*, i32*, i16*, i8, [256 x i8], [256 x i8], i8, [6 x i8] }>

%"class.std::locale::facet.base" = type <{ i32 (...)**, i32 }>

%struct.__locale_struct = type { [13 x %struct.__locale_data*], i16*, i32*, i32*, [13 x i8*] }

%struct.__locale_data = type opaque

%"class.std::num_put" = type { %"class.std::locale::facet.base", [4 x i8] }

%"class.std::num_get" = type { %"class.std::locale::facet.base", [4 x i8] }



@_ZStL8__ioinit = internal global %"class.std::ios_base::Init" zeroinitializer, align 1

@__dso_handle = external hidden global i8

@_ZSt4cout = external dso_local global %"class.std::basic_ostream", align 8

@.str = private unnamed_addr constant [13 x i8] c"Hello World!\00", align 1

@llvm.global_ctors = appending global [1 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 65535, void ()* @_GLOBAL__sub_I_hello.cpp, i8* null }]



; Function Attrs: noinline uwtable

define internal void @__cxx_global_var_init() #0 section ".text.startup" {

  call void @_ZNSt8ios_base4InitC1Ev(%"class.std::ios_base::Init"* nonnull dereferenceable(1) @_ZStL8__ioinit)

  %1 = call i32 @__cxa_atexit(void (i8*)* bitcast (void (%"class.std::ios_base::Init"*)* @_ZNSt8ios_base4InitD1Ev to void (i8*)*), i8* getelementptr inbounds (%"class.std::ios_base::Init", %"class.std::ios_base::Init"* @_ZStL8__ioinit, i32 0, i32 0), i8* @__dso_handle) #3

  ret void

}



declare dso_local void @_ZNSt8ios_base4InitC1Ev(%"class.std::ios_base::Init"* nonnull dereferenceable(1)) unnamed_addr #1



; Function Attrs: nounwind

declare dso_local void @_ZNSt8ios_base4InitD1Ev(%"class.std::ios_base::Init"* nonnull dereferenceable(1)) unnamed_addr #2



; Function Attrs: nounwind

declare dso_local i32 @__cxa_atexit(void (i8*)*, i8*, i8*) #3



; Function Attrs: noinline norecurse optnone uwtable mustprogress

define dso_local i32 @main() #4 {

  %1 = alloca i32, align 4

  store i32 0, i32* %1, align 4

  %2 = call nonnull align 8 dereferenceable(8) %"class.std::basic_ostream"* @_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc(%"class.std::basic_ostream"* nonnull align 8 dereferenceable(8) @_ZSt4cout, i8* getelementptr inbounds ([13 x i8], [13 x i8]* @.str, i64 0, i64 0))

  %3 = call nonnull align 8 dereferenceable(8) %"class.std::basic_ostream"* @_ZNSolsEPFRSoS_E(%"class.std::basic_ostream"* nonnull dereferenceable(8) %2, %"class.std::basic_ostream"* (%"class.std::basic_ostream"*)* @_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_)

  ret i32 0

}



declare dso_local nonnull align 8 dereferenceable(8) %"class.std::basic_ostream"* @_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc(%"class.std::basic_ostream"* nonnull align 8 dereferenceable(8), i8*) #1



declare dso_local nonnull align 8 dereferenceable(8) %"class.std::basic_ostream"* @_ZNSolsEPFRSoS_E(%"class.std::basic_ostream"* nonnull dereferenceable(8), %"class.std::basic_ostream"* (%"class.std::basic_ostream"*)*) #1



declare dso_local nonnull align 8 dereferenceable(8) %"class.std::basic_ostream"* @_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_(%"class.std::basic_ostream"* nonnull align 8 dereferenceable(8)) #1



; Function Attrs: noinline uwtable

define internal void @_GLOBAL__sub_I_hello.cpp() #0 section ".text.startup" {

  call void @__cxx_global_var_init()

  ret void

}



attributes #0 = { noinline uwtable "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" "unsafe-fp-math"="false" "use-soft-float"="false" }

attributes #1 = { "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" "unsafe-fp-math"="false" "use-soft-float"="false" }

attributes #2 = { nounwind "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" "unsafe-fp-math"="false" "use-soft-float"="false" }

attributes #3 = { nounwind }

attributes #4 = { noinline norecurse optnone uwtable mustprogress "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" "unsafe-fp-math"="false" "use-soft-float"="false" }



!llvm.module.flags = !{!0}

!llvm.ident = !{!1}



!0 = !{i32 1, !"wchar_size", i32 4}

!1 = !{!"clang version 12.0.1"}
第二步:优化LLVM IR

使用opt指令对LLVM IR进行优化

opt -load LLVMObfuscator.so -hlw -S hello.ll -o hello_opt.ll

  • -load 加载特定的LLVM Pass(集合)进行优化(通常为.so文件)
  • -hlw是LLVM Pass中自定义的参数,用来指定使用哪个Pass进行优化
第三步:编译LLVM IR为可执行文件

这一步我们通过Clang完成,从LLVM IR到可执行文件中间还有一系列复杂的流程,Clang帮助我们整合了这个过程

clang hello_opt.ll -o hello

参考

posted @ 2022-04-14 11:31  Tu9oh0st  阅读(7307)  评论(0编辑  收藏  举报