如何读写LLVM bitcode

如何读写LLVM bitcode

本文翻译自How to read & write LLVM bitcode

我已经在社交媒体上读了很多帖子,它们都在抱怨LLVM太可怕了。Repository非常庞大,每天提交数百次,邮件列表几乎不可能跟踪,生成的可执行文件现在已经超过40Mb,这对解决问题没有任何帮助。
撇开这些麻烦不谈,一旦你能抓住重点,LLVM就非常容易处理。为了帮助人们使用LLVM,我想我应该把用LLVM可以做的最简单的no-op示例放在一起 —— 解析LLVM的一个中间表示文件(称为bitcode,文件扩展名.bc),然后将其写出来。

首先,让我们来看一些高级LLVM术语:

  • LLVM对用户代码的主要抽象是Module。它是一个类,包含您或其他用户编写的代码的所有函数、全局变量和指令。
  • bitcode文件实际上是LLVM Module的序列化,以便以后可以在不同的程序中重建它。
  • LLVM使用MemoryBuffer对象来处理来自文件、stdin或数组的数据。

对于我的例子,我们将使用LLVM C API —— 一个在LLVM的核心C++ header之上的更稳定的抽象。如果您希望使用多个版本的LLVM,那么C API非常有用,它比LLVM C++ header稳定得多。(顺便说一句,我在工作中大量使用LLVM,几乎每周都会有一些LLVM C++ header的更改会破坏我们的代码。我从来没有过C API破坏过我的代码。

首先,我假设您已经下载了LLVM,构建并安装了它。下面是这个过程简单的步骤:

git clone https://git.llvm.org/git/llvm.git <llvm dir>
cd <llvm dir>
mkdir build
cd build
cmake -DCMAKE_BUILD_TYPE=RELEASE -DCMAKE_INSTALL_PREFIX=install ..
cmake --build . --target install

完成上述操作后,您将在/build/install中安装LLVM !

对于我们的小型的可执行文件,我使用了CMake。CMake是目前为止与LLVM集成最简单的方法,因为它也是LLVM所使用的构建系统。

project(llvm_bc_parsing_example)
cmake_minimum_required(VERSION 3.4.3)

# option to allow a user to specify where an LLVM install is on the system
set(LLVM_INSTALL_DIR "" CACHE STRING "An LLVM install directory.")

if("${LLVM_INSTALL_DIR}" STREQUAL "")
  message(FATAL_ERROR "LLVM_INSTALL_DIR not set! Set it to the location of an LLVM install.")
endif()

# fixup paths to only use the Linux convention
string(REPLACE "\\" "/" LLVM_INSTALL_DIR ${LLVM_INSTALL_DIR})

# tell CMake where LLVM's module is
list(APPEND CMAKE_MODULE_PATH ${LLVM_INSTALL_DIR}/lib/cmake/llvm)

# include LLVM
include(LLVMConfig)

add_executable(llvm_bc_parsing_example main.c)

target_include_directories(llvm_bc_parsing_example PUBLIC ${LLVM_INCLUDE_DIRS})

target_link_libraries(llvm_bc_parsing_example PUBLIC LLVMBitReader LLVMBitWriter)

现在我们有了CMake设置,我们可以使用现有安装的LLVM,现在我们可以开始实际的C代码了!
所以要使用LLVM C API,你基本上总是需要一个头文件:

#include <llvm-c/Core.h>

和我们需要为我们的可执行文件准备两个额外的头文件,分别是bitcode reader和writer:

#include <llvm-c/BitReader.h>
#include <llvm-c/BitWriter.h>

现在我们创建main函数。这里我假设我们总是取恰好两个命令行参数,第一个是输入文件,第二个是输出文件。LLVM有一个系统,如果提供一个名为“-”的文件,这意味着从stdin读取或写入stdout,所以我决定也支持它:

if (3 != argc) {
  fprintf(stderr, "Invalid command line!\n");
  return 1;
}

const char *const inputFilename = argv[1];
const char *const outputFilename = argv[2];

首先我们解析输入文件。我们将从stdin或文件名中创建一个LLVM内存缓冲区对象:

LLVMMemoryBufferRef memoryBuffer;

// check if we are to read our input file from stdin
if (('-' == inputFilename[0]) && ('\0' == inputFilename[1])) {
  char *message;
  if (0 != LLVMCreateMemoryBufferWithSTDIN(&memoryBuffer, &message)) {
    fprintf(stderr, "%s\n", message);
    free(message);
    return 1;
  }
} else {
  char *message;
  if (0 != LLVMCreateMemoryBufferWithContentsOfFile(
               inputFilename, &memoryBuffer, &message)) {
    fprintf(stderr, "%s\n", message);
    free(message);
    return 1;
  }
}

因此,在这段代码之后,memoryBuffer将可用来将我们的字节码文件读入LLVM Module。因此,让我们开始创建Module

// now create our module using the memory buffer
LLVMModuleRef module;
if (0 != LLVMParseBitcode2(memoryBuffer, &module)) {
  fprintf(stderr, "Invalid bitcode detected!\n");
  LLVMDisposeMemoryBuffer(memoryBuffer);
  return 1;
}

// done with the memory buffer now, so dispose of it
LLVMDisposeMemoryBuffer(memoryBuffer);

一旦我们有了我们的Module,我们不再需要内存缓冲区,所以我们可以立即释放内存。这是它!我们已经成功地获取了一个LLVM bitcode文件,并将其反序列化到LLVM Module中,我们可以(至少在本文中我不会这么做!)因此,假设您已经对LLVM Module做了所有想要做的事情,并且希望再次将sucker写回bitcode文件。

该方法与读取方法正交,我们寻找特殊的文件名’ - ',并相应地处理:

// check if we are to write our output file to stdout
if (('-' == outputFilename[0]) && ('\0' == outputFilename[1])) {
  if (0 != LLVMWriteBitcodeToFD(module, STDOUT_FILENO, 0, 0)) {
    fprintf(stderr, "Failed to write bitcode to stdout!\n");
    LLVMDisposeModule(module);
    return 1;
  }
} else {
  if (0 != LLVMWriteBitcodeToFile(module, outputFilename)) {
    fprintf(stderr, "Failed to write bitcode to file!\n");
    LLVMDisposeModule(module);
    return 1;
  }
}

最后,我们应该做一个好公民,清理我们的垃圾,所以也删除模块:

LLVMDisposeModule(module);

这是它!现在您可以解析并编写LLVM bitcode文件。我已经将完整的示例放在了GitHub上——https://github.com/sheredom/llvm_bc_parsing_example。

也许我会在某个时候写一篇后续文章,向您介绍如何对LLVM Module执行操作的基础知识。

猜你喜欢

转载自blog.csdn.net/qq_23599965/article/details/88294618