01_llvm编译及创建一个module试用llvm
LLVM源码编译
准备好匹配的环境后,我的环境如下:
$ cat /proc/version
Linux version 5.4.0-150-generic (buildd@bos03-amd64-012) (gcc version 7.5.0 (Ubuntu 7.5.0-3ubuntu1~18.04)) #167~18.04.1-Ubuntu
如果环境较老,可checkout到比较老的分支再编,不然容易出现各种软件版本不匹配的问题。
根据官网指导编译过程比较简单
#1下代码
git clone https://github.com/llvm/llvm-project.git
git checkout remotes/origin/release/13.x -b release_13
mkdir build
cd build
#2配置
# 不编clang,-DCMAKE_BUILD_TYPE=Debug编成调试模式
cmake -S ../llvm -G Ninja -DCMAKE_BUILD_TYPE=Debug
# 指定安装在xxxx/llvm-project/output目录下,若不设定则默认Install在/usr/local
cmake -S ../llvm -G Ninja -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX="xxxx/llvm-project/output"
# 同时编clang
cmake -S ../llvm -G Ninja -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX="xxx/llvm-project/output" -DLLVM_ENABLE_PROJECTS="clang;lld"
#3编译/安装
ninja
# 编译并安装到上边指定目录
ninja install
试用,源码,编译
int add_func(int a) {
return a + 33;
}
// ../llvm-project/output/bin/clang -S -emit-llvm 01_test_ir.c -o 01_test_.ll
生成的ir如下
; ModuleID = '01_test_ir.c'
source_filename = "01_test_ir.c"
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"
; Function Attrs: noinline nounwind optnone uwtable
define dso_local i32 @add_func(i32 %a) #0 {
entry:
%a.addr = alloca i32, align 4
store i32 %a, i32* %a.addr, align 4
%0 = load i32, i32* %a.addr, align 4
%add = add nsw i32 %0, 33
ret i32 %add
}
attributes #0 = { noinline nounwind optnone uwtable "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
!llvm.module.flags = !{!0, !1, !2}
!llvm.ident = !{!3}
!0 = !{i32 1, !"wchar_size", i32 4}
!1 = !{i32 7, !"uwtable", i32 1}
!2 = !{i32 7, !"frame-pointer", i32 2}
!3 = !{!"clang version 13.0.1 (https://github.com/llvm/llvm-project.git 75e33f71c2dae584b13a7d1186ae0a038ba98838)"}
试用:创建一个module
使用就是利用llvm提供的基础功能,构建出自己的类似于程序分析、ebpf项目或者编译器等。
llvm里的module是一个llvm IR里的数据结构,简单理解,一个最简单的程序,编译后生成的ir会是一个module节点,把下属函数IR等放入其中。
源码01_test_module.cpp
#include "llvm/IR/Module.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/Support/raw_ostream.h"
using namespace llvm;
int main() {
LLVMContext c;
Module *m = new Module("test module", c);
m->print(outs(), nullptr);
return 0;
}
编译脚本01_build_test_module.sh
CLANG_PATH="../output/bin/clang++"
${CLANG_PATH} -w -o test_modu_bin `llvm-config --cxxflags --ldflags --system-libs --libs core` ./01_test_module.cpp
执行01_build_test_module.sh编译后会在当前文件夹产生一个test_modu_bin,执行后可得如下结果:
; ModuleID = 'test module'
source_filename = "test module"
编译中遇到的报错
- outs()找不到
./01_test_module.cpp:9:12: error: use of undeclared identifier 'outs'
m->print(outs(), nullptr);
^
1 error generated.
因为缺少头文件,在源码搜下可发现在“llvm/Support/raw_ostream.h:577:raw_fd_ostream &outs();”
- 对‘operator new(unsigned long)’未定义的引用
01_test_module-64f105.o:01_test_module.cpp:function main: 错误: 对‘operator new(unsigned long)’未定义的引用
clang-13: error: linker command failed with exit code 1 (use -v to see invocation)
错误看起来是不认识new,是因编译命令使用的是clang,不是clang++,以为这只是个软链接,用哪个都没啥区别,但搜了下资料说使用g++默认会链接libc++,而使用gcc时不会,可见针对clang++也一样。