01_llvm编译及创建一个module试用llvm

发布时间 2023-07-29 14:55:37作者: UFOFCZ

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

#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

export SDKROOT="../output/"
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"

编译中遇到的报错

  1. 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();”

  1. 对‘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++也一样。