llvm 是什么
low level virtual machine , 是个编译器。
llvm的设计
计算机常用解决方法。百分之80的性能和功能问题都可以通过添加一个缓冲区来完成,llvm也是使用了这种方法,添加了IR中间语言。
source code -> IR -> IR -> target-indepent code (代码层面)。
学习的目的
在函数中插入指令
该指令为框架相关的指令。
需要的知识点
IR 语法和结构
语法
以helloworld为例
#include <stdio.h>
int main(void){
printf("Hello World\n");
return 0;
}
编译为IR中间语言
结构
模块module
个人理解 一个文件就可以当作一个模块
函数
函数就是函数
块
一个函数由代码块组成
指令
块由指令组成
遍历HelloWorld.ll 的模块、函数、块、指令,得到以下结果:
Module Name: HelloWorld.ll
Function Name: main
BasicBlock Name:
OpccodeName: alloca
OpccodeName: store
OpccodeName: call
OpccodeName: ret
Function Name: printf
遍历代码:
errs() << "Module Name:\t" << M.getName() << "\n";
for(auto &F : M){
errs() << "Function Name:\t" <<F.getName() << "\n";
for(auto &B : F){
errs() << "BasicBlock Name:\t" <<B.getName() << "\n";
B.setName("mc");
for (Instruction &I : B)
errs() << "OpccodeName:\t" <<I.getOpcodeName() << "\n";
}
}
PASS
IR到IR的处理,看个简单的PASS
用InjectFuncCall实例来分析pass,源代码在https://github.com/banach-space/llvm-tutor/blob/master/lib/InjectFuncCall.cpp , InjectFuncCall的功能如下:
(llvm-tutor) Hello from: main
(llvm-tutor) number of arguments: 0
Hello World
从C代码来看就是插入了类似这样的代码。
printf(“(llvm-tutor) Hello from: %s\n(llvm-tutor) number of arguments: %s”,funcname ,arg_num);
再看源码之前,我们先来分析下在插入一个函数调用需要什么,如下:
- 调用指令
- 函数返回值
- 函数名称
- 参数
- 参数类型
- 参数名称
- 参数个数
然后再来看PASS 操作IR 添加函数调用就很简单了。
bool InjectFuncCall::runOnModule(Module &M) { bool InsertedAtLeastOnePrintf = false; //获取llvmContext auto &CTX = M.getContext(); //创建一个函数参数类型 PointerType *PrintfArgTy = PointerType::getUnqual(Type::getInt8Ty(CTX)); FunctionType *PrintfTy = FunctionType::get( IntegerType::getInt32Ty(CTX), PrintfArgTy, /*IsVarArgs=*/true); FunctionCallee Printf = M.getOrInsertFunction("printf", PrintfTy); //获取调用printf的函数 Function *PrintfF = dyn_cast<Function>(Printf.getCallee()); errs()<<PrintfF.getCallee()<<"\n"; // 插入名称为PrintfFormatStr 的全局变量 llvm::Constant *PrintfFormatStr = llvm::ConstantDataArray::getString( CTX, "(llvm-tutor) Hello from11: %s\n(llvm-tutor) number of arguments: %d\n"); Constant *PrintfFormatStrVar = M.getOrInsertGlobal("PrintfFormatStr", PrintfFormatStr->getType()); dyn_cast<GlobalVariable>(PrintfFormatStrVar)->setInitializer(PrintfFormatStr); for (auto &F : M) { //用来区分是否为引用函数。 if (F.isDeclaration()) continue; // Get an IR builder. Sets the insertion point to the top of the function IRBuilder<> Builder(&*F.getEntryBlock().getFirstInsertionPt()); // Inject a global variable that contains the function name //创建全局变量并获取他的指针。 auto FuncName = Builder.CreateGlobalStringPtr(F.getName()); // Printf requires i8*, but PrintfFormatStrVar is an array: [n x i8]. Add // 创建指向PrintfFormatStrVar 参数值 PrintfArgTy 参数类型的指针。 第三个参数"formatStr"似乎是可选参数,删除后不影响操作。 llvm::Value *FormatStrPtr = Builder.CreatePointerCast(PrintfFormatStrVar, PrintfArgTy, "formatStr"); // The following is visible only if you pass -debug on the command line // *and* you have an assert build. LLVM_DEBUG(dbgs() << " Injecting call to printf inside " << F.getName() << "\n"); // 这段代码比较明显,创建一个函数调用,结合参数了解到创建函数调用我们需要调用的函数和函数参数。 Builder.CreateCall( Printf, {FormatStrPtr, FuncName, Builder.getInt32(F.arg_size())}); InsertedAtLeastOnePrintf = true; } return InsertedAtLeastOnePrintf; }
0 条评论