1、环境
macOS 10.15.7
Xcode 11.3
LLVM 8.0.0(下载地址:releases.llvm.org/download.ht…
2、本地下载LLVM源码,生成Xcode项目
(1)下载llvm8.0.0
访问 releases.llvm.org/download.ht… ,找到8.0.0的源码:
我这里是用Xcode11.3,所以下载LLVM8.0.0,如果你用Xcode12可以下载LLVM10.0.0,不同的Xcode版本对应不同的LLVM版本。可以在这里(en.wikipedia.org/wiki/Xcode)查看对应关系。
(2)编译生成Xcode工程
已下载的LLVM8.0.0文件,解压到你的任意路径下,在同级目录下创建一个 xcode_build_8.0 文件夹,并打开终端 cd 到xcode_build_8.0 路径下,并使用cmake命令生成Xcode工程:
cmake -G Xcode CMAKE_BUILD_TYPE="Debug" ../llvm
双击 LLVM.xcodeproj 文件打开工程,按照下图选择:
找到 ALL_BUILD ,并执行编译(快捷键:comd+B),编译整个LLVM工程,需要较长的时间:
编译完成后,可在Debug路径下看到编译后的文件:
3、编写一个pass
(1)在LLVM工程中找到测试用例Pass项目Hello:
在Hello.cpp文件代码如下:
#include "llvm/IR/IRBuilder.h"#include "llvm/IR/IntrinsicInst.h#include "llvm/ADT/Statistic.h"#include "llvm/IR/Function.h"#include "llvm/Pass.h"#include "llvm/Support/raw_ostream.h"#include "llvm/Analysis/LoopInfo.h"#include "llvm/Analysis/LoopPass.h"#include "llvm/PassAnalysisSupport.h"
using namespace llvm;#define DEBUG_TYPE "hello"STATISTIC(HelloCounter, "Counts number of functions greeted");
//这个pass的功能:当 c 文件里没有 main 方法的时候,自动生成 main并且在main中调用当前 module 里的所有函数。
//思路是,先看看有没有 main,有的话就终止操作,没有的话就创造一个 main,之后创建BB、创建Inst、完成调用。
namespace { class SimpleInvoker : public ModulePass { public: static char ID; SimpleInvoker() : ModulePass(ID) {} bool runOnModule(Module &M) override{ Function *F = M.getFunction("main"); if (F) { errs() << "Main Function Found! So return.\n"; return true; } errs() << "Main Function Not Found! So create.\n"; FunctionType *FT = FunctionType::get(Type::getInt32Ty(M.getContext()), false); F = Function::Create(FT, GlobalValue::LinkageTypes::ExternalLinkage, "main", &M); BasicBlock *EntryBB = BasicBlock::Create(M.getContext(), "EntryBlock", F); IRBuilder<> IRB(EntryBB); for (Function &FF:M) { if(FF.getName() == "main") continue; if(FF.empty()) continue; IRB.CreateCall(&FF); } IRB.CreateRet(ConstantInt::get(Type::getInt32Ty(M.getContext()), 0)); return true; }
virtual StringRef getPassName() const override { return "Simple Invoker"; } }; Pass *createSimpleInvoker() { return new SimpleInvoker(); } }
char SimpleInvoker::ID = 0;static RegisterPass<SimpleInvoker> Y("simpleInvoker", "SimpleInvoker Pass");
(2)Xcode选中LLVMHello 并编译,生成 LLVMHello.dylib 文件:
这就是我们编写的pass了。这个pass的功能是当 c 文件里没有 main 方法的时候,自动生成 main并且在main中调用当前 module 里的所有函数。
4、测试pass功能
(1)创建test.c文件
这个文件中没有main函数,只用f1()和f2()两个方法,代码如下:
void f1(){
printf("f1--printf\n");
}
int f2(){
printf("f2---printf\n");
return 0;
}
(2)用 Xcode 中的 clang 编译 test.c 生成 test.bc 中间码文件
/path/to/clang -emit-llvm -c ./test.c -o ./test.bc
(3)使用 opt 加载 LLVMHello.dylib 处理 test.bc 中间码文件,得到 test1.bc 文件
/path/to/xcode_build_8.0/Debug/bin/opt -load /path/to/xcode_build_8.0/Debug/lib/LLVMHello.dylib -simpleInvoker ./test.bc -o ./test1.bc
这个 opt 在是你LLVM工程编译后生成的一个可执行文件。
对test.bc的中间码做了处理,遍历是否包含main函数,不过不包含则插入一个main函数并在其中调用f1()和f2()这两个方法,生成test1.bc 文件。
(4)用 lli 执行 test1.bc 文件
/path/to/xcode_build_8.0/Debug/bin/lli ./test1.bc
这个 lli 在是你LLVM工程编译后生成的一个可执行文件。
终端输出:
f1--printf
f2---printf
说明正确添加了main()函数,并成功调用了f1()和f2()这两个方法。
本文部分内容参考自: